【问题标题】:Execute multiple stored procedures with single trip to database一次访问数据库执行多个存储过程
【发布时间】:2011-05-20 17:22:44
【问题描述】:

我有很多遗留数据访问代码,主要是带有存储过程调用的 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?

提前致谢

【问题讨论】:

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


    【解决方案1】:

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

    CREATE PROCEDURE sp_RunBatch(@param1, @param2, etc [all the parameters you need]) AS exec spMyStoreProc @a='a' exec spMyStoreProc2 @b='b'

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

    另外,如果您不想将 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

    【讨论】:

    • 实际上,调用之间的参数差异很大,因此使用包装器 SP 的第一个解决方案并不能真正解决我的“问题”。我将研究表格方法,这似乎是一个有趣的解决方案。
    【解决方案2】:

    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;
          }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-28
      • 1970-01-01
      • 1970-01-01
      • 2020-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多