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