我有一个非常简单的存储过程:
create procedure spFoo(v varchar(50))
as
insert into tbFoo select v
我有50个值要插入到tbFoo中,这意味着在我的c#代码中我将spFoo调用了50次。这是一种非常低效的方法,特别是如果我的程序和数据库之间存在一些延迟。
在这种情况下你通常做什么?
我正在使用SQL Server 2008但它可能不相关。
如果您的问题是尝试传递多行,那么从SQL Server 2008开始,您有一个新的参数类型Table-Valued。这允许您通过类型结构化的.NET SQLParamter将.Net数据表直接传递到存储过程。
tvpParam.SqlDbType = SqlDbType.Structured
但是,如果问题是您尝试填充的是一行中有50列,那么您最好将它们全部作为单独的参数传递并更改过程而不是尝试使用代码或T-SQL。
有一个很好的article演示如何在SQL Server中使用表值参数,并通过.NET在C#和VB.Net中使用。希望这可以帮助。
实际上,SQL Server 2008的一个很酷的功能是table valued parameters。
建立:
create type fooTableType as table (v varchar(50));
go
create proc spFoo(@t fooTableType readonly) as
insert into tbFoo(v) select v from @t
go
然后你的C#代码gens:
declare @t as fooTableType
insert @t(v) values('beyond'),('all'),('recognition')
exec spFoo(@t)
这里有一些粗略的C#代码:
using (SqlCommand cmd=new SqlCommand()) {
StringBuilder sql = new StringBuilder();
for(int i=0; i<50; i++) {
sql.AppendFormat(",(@v{i})",i);
cmd.Parameters.AddWithValue("@v"+i.ToString(),values[i]);
}
cmd.CommandText = "declare @t as fooTableType; insert @t(v) values" +
sql.ToString().Substr(1) + "; exec spFoo(@t);"
cmd.CommandType = CommandType.Text;
cmd.Connection = myConnection;
cmd.ExecuteNonQuery();
}
我一般会同意这种优化并非绝对必要。也许它是强迫症,但我不能把它留作cmd.ExecuteNonQuery()的循环。
查看CertifiedCrazy的answer以获得更简洁的C#代码版本。
另请注意,此技术仅限于2100个总参数 - 这是底层TDS协议的限制。
实际上,这里效率低下了什么?如果您不想使用任何类型的BULK INSERT,则必须将其调用50次。尽管如此,如果您使用了预准备语句,并且只是在每次运行时替换参数值,那么它应该非常快。
此外,50个插入/ sp调用实际上没什么。马上就去吧。
这取决于这是否是性能瓶颈。 如果不是,我不担心。
如果是,您可以将所有50个项目传递给一个存储过程,例如一个字符串。 然后,存储过程可以拆分值,然后根据需要调用您的创建过程。 所以你会调用SQL一次,它会做50次插入。
通过创建参数@ v0到@ v49来批量调用,所以它只有1次网络往返:
using (var cmd = new SqlCommand()) {
for (int i = 0; i < 50; i++) {
string paramName = "@v" + i.ToString();
cmd.CommandText += string.Format("EXEC spFoo " + paramName + ";");
cmd.Parameters.AddWithValue(paramName, values[i]);
}
cmd.ExecuteNonQuery();
}
我将列表构造为xml字符串并将其传递给存储过程。在SQL 2005中,它具有良好的xml功能,可以解析xml并执行批量插入。
将要插入的值作为XML参数传递。然后,您可以使用XPath查询批量插入数据。