通过一次数据库访问执行多个存储过程

发布于 2024-11-09 02:29:09 字数 1118 浏览 2 评论 0原文

我有很多遗留数据访问代码,主要是 SqlCommand 和存储过程调用,我们用它们来执行大量插入语句到数据库中。 只要 SQL 服务器与应用程序位于同一台计算机上,就有可接受的性能,但现在我们正在尝试将一些数据移动到 SQL Azure。

问题是我们的代码为每条要插入的记录调用 SP,这会导致多次访问数据库,并且当不在同一服务器上时,需要一些时间。

var conn = new SqlConnection("connString")
var cmd = new SqlCommand(conn, "spMyStoreProc");
cmd.Params.Add("@a", SqlDbType.VarChar, 10);
cmd.Params.Add("@b", SqlDbType.Int);

using(conn)
{
 conn.Open();
 foreach(var rec in recordsToInsert)
 {
   cmd.Parameters["@a"].Value = rec.A;
   cmd.Parameters["@b"].Value = rec.B;
   cmd.ExecuteNonQuery();
 }
 conn.Close();
}

我已经尝试了上面的代码,无论是否有事务。

我还尝试使用“批处理”SQL 语句在每次访问服务器时执行多个 SP。 像这样:

var cmd = new SqlCommand(conn);
cmd.CommandText = "EXEC spMyStoreProc @a='a' @b=2; EXEC spMyStoreProc @a='b' @b=4;"

它极大地提高了操作的性能,但由于我有相当多的 SP,其中每个 SP 大约有 20-50 个参数,因此为该数据访问组件中的所有插入命令编写此代码变得非常乏味。

这是实现此目的的最佳方法吗?或者我可以以某种方式告诉 ADO.NET 我想批量执行我的调用(没有找到任何暗示其可能的内容,但觉得我至少应该问一下)以避免每个 SP 之间的网络延迟等称呼?

如果没有,没有人知道有什么好方法可以实现这一点,而不必“手工”编写它,并且由于它是遗留应用程序,我无法完全更改数据层。 是否有任何应用程序可以采用带参数的 SqlCommands 并生成它们将执行的 TQL?

提前致谢

I have a lot of legacy data access code mainly SqlCommand with Stored Procedure calls that we used to execute alot of Insert statment into an database.
As long as the SQL server has been on the same machine as the application there have been acceptable performace but now are we trying to move some of the data to SQL Azure.

The problem is that our code calls a SP for every record to insert which results in quite a few trips to the database and when not located on the same server it takes some time.

var conn = new SqlConnection("connString")
var cmd = new SqlCommand(conn, "spMyStoreProc");
cmd.Params.Add("@a", SqlDbType.VarChar, 10);
cmd.Params.Add("@b", SqlDbType.Int);

using(conn)
{
 conn.Open();
 foreach(var rec in recordsToInsert)
 {
   cmd.Parameters["@a"].Value = rec.A;
   cmd.Parameters["@b"].Value = rec.B;
   cmd.ExecuteNonQuery();
 }
 conn.Close();
}

I have tried the code above with and without Transactions.

I have also tried to use a "batch" SQL statement to execute several SPs in every trip to the server.
Like this:

var cmd = new SqlCommand(conn);
cmd.CommandText = "EXEC spMyStoreProc @a='a' @b=2; EXEC spMyStoreProc @a='b' @b=4;"

It greatly increases the performance of the operation but since I have quite a few SPs where every SP has about 20-50 params it gets quite tedious to write this code for all the insert commands in this data access component.

Is this the best way to achive this, or can I somehow tell ADO.NET I want to execute my calls as a batch (havent fount anything suggesting its possible but feel that I atleast should ask) to avoid network latency etc betweeen every single SP call?

If not does anybody know any good way to achive this without having to write it "by hand" and since its a legacy application I can not change the data layer completely.
Is there any applications that can take SqlCommands with parameters and generate the TQL they would execute?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

天赋异禀 2024-11-16 02:29:09

您可能应该有一个存储过程,它调用所有其他存储过程 - 这可能是最少的工作量。因此,从代码中,您只调用存储过程一次......因此,鉴于它们是您每次传递的相同参数(因为您的代码似乎暗示了这一点),您基本上会做这样的事情:


CREATE PROCEDURE sp_RunBatch(@param1, @param2, etc [all the parameters you need])
AS

exec spMyStoreProc @a='a'
exec spMyStoreProc2 @b='b' 


这样做的优点是很多,其中一些是全部集中的,您甚至可以将它们全部包装在一个事务中,以免进行脏插入(假设它们都相互依赖)。

另外,如果您不想向每个 SP 传递 20/30 个参数,您可能需要为您可以传递的每组参数创建一个用户表定义的数据类型。这样每个 SP 就获得 1 或 2 个参数,代码变得更加简单和可读。

编辑:

这是用户定义的表类型的一个很好的参考:http:// msdn.microsoft.com/en-us/library/bb675163.aspx

这是将表值类型传递到 SQL Server 的方法:http://msdn.microsoft.com/en-us/library/bb675163.aspx

You should probably have one stored procedure, that calls all the other stored procedures - it will probably be the least amount of work. So, from the code you only call the stored procedures once... so given that they are the same parameters you are passing every time (because your code seems to imply that) you would basically do something like this:


CREATE PROCEDURE sp_RunBatch(@param1, @param2, etc [all the parameters you need])
AS

exec spMyStoreProc @a='a'
exec spMyStoreProc2 @b='b' 


The advantages of this are many, some of which being that its all centralized, and you can even wrap all of them within a transaction, so as not to do dirty inserts (given that they all depend on each other).

Also, if you don't feel like passing 20/30 parameters to each SP, you may want to make a user-table-defined data-type for each set of parameters, that you can pass. So then each SP gets 1 or 2 parameters, and the code becomes much simpler and readable.

EDIT:

This is a good reference for the user-defined table types: http://msdn.microsoft.com/en-us/library/bb675163.aspx

And this is how to pass the table valued types to SQL server: http://msdn.microsoft.com/en-us/library/bb675163.aspx

昔日梦未散 2024-11-16 02:29:09

MR 方法的替代方法是将所有参数作为 XML 文档发送,然后解析 XML 文档以提取参数。这可能会稍微简化界面。

不过,当您讨论将所有命令链接到单个字符串中的可能性时,我认为您正在做一些事情。但不要手动构建它们,而是考虑构建 SqlCommand 对象的扩展方法,该方法返回单个字符串以供执行,利用 sp_executesql 语法,并在一次传递中执行整个字符串。

因此,您将有一个如下所示的循环,并且您将调用一个新的 ToInlineSql 扩展方法:

string sqlCommand = "";
foreach(var rec in recordsToInsert) 
{   cmd.Parameters["@a"].Value = rec.A;   
    cmd.Parameters["@b"].Value = rec.B;
    sqlCommand += cmd.ToInlineSql(); 
}
// execute sqlCommand 

ToInlineSql 扩展方法可能如下所示(peuso 代码,您必须添加某些内容,例如检查数据类型等上)[这里是 sp_executesql

public static class SqlCmdExt 
{
      public static string ToInlineSql(this SqlCommand cmd)
      {
            string sql = "sp_executesql " + cmd.CommandText  ;
            foreach (SqlParameter p in cmd.Parameters)
            {
                  sql += ", @" + p.Name + " " + p.DataType.ToString() ;
                  sql += ", " + p.Name + " = " + p.Value;
            }
            sql += ";";
            return sql;
      }
}

An alternative to M.R.'s approach would be to send all your parameters as an XML document, then parse the XML document to extract your parameters. This may simplify the interface a bit.

However I think you were on something when you discussed the possibility of chaining all the commands in a single string. But instead of manually building them, consider building an extension method to the SqlCommand object that returns a single string for execution, leveraging the sp_executesql syntax, and execute the entire string in a single pass.

So you would have a loop that looks like this, and you would call a new ToInlineSql extension method:

string sqlCommand = "";
foreach(var rec in recordsToInsert) 
{   cmd.Parameters["@a"].Value = rec.A;   
    cmd.Parameters["@b"].Value = rec.B;
    sqlCommand += cmd.ToInlineSql(); 
}
// execute sqlCommand 

The ToInlineSql extension method could look like this (peuso-code, you will have to add certain things such as checking for the data type and so on) [and here is the link to sp_executesql:

public static class SqlCmdExt 
{
      public static string ToInlineSql(this SqlCommand cmd)
      {
            string sql = "sp_executesql " + cmd.CommandText  ;
            foreach (SqlParameter p in cmd.Parameters)
            {
                  sql += ", @" + p.Name + " " + p.DataType.ToString() ;
                  sql += ", " + p.Name + " = " + p.Value;
            }
            sql += ";";
            return sql;
      }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文