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

时间:2011-05-20 17:22:44

标签: sql-server tsql ado.net azure-sql-database

我有很多遗留数据访问代码,主要是带有存储过程调用的SqlCommand,我们用它来执行很多Insert statment到数据库中。 只要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();
}

我在使用和不使用Transactions时尝试过上面的代码。

我还尝试使用“批处理”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?

提前致谢

2 个答案:

答案 0 :(得分:2)

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


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

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


这样做的好处很多,其中一些是集中的,你甚至可以将它们全部包装在一个事务中,以便不进行脏插入(假设它们彼此依赖)。 / p>

此外,如果您不想将20/30参数传递给每个SP,您可能希望为每组参数创建一个用户表定义的数据类型,您可以传递这些参数。因此,每个SP获得1或2个参数,代码变得更加简单和可读。

编辑:

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

这就是如何将表值类型传递给SQL服务器:http://msdn.microsoft.com/en-us/library/bb675163.aspx

答案 1 :(得分:0)

M.R.方法的替代方法是将所有参数作为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-code,你必须添加某些东西,比如检查数据类型等等)[这里是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;
      }
}