【发布时间】:2009-08-28 18:46:52
【问题描述】:
SqlCommand 的 CommandText 的长度有影响吗?我也不是在谈论数千个字符。这是我所拥有的:
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = sql;
for (int i=0; i<1000; ++i)
{
string name = i.ToString() + "Bob" + i.ToString();
string email = i.ToString() + "Jim" + i.ToString();
// etc...
cmd.Parameters.Clear();
cmd.Parameters.Add(new SqlParameter("@name", name));
cmd.Parameters.Add(new SqlParameter("@country", country));
DateTime cmdStart = DateTime.Now;
cmd.ExecuteNonQuery();
DateTime cmdEnd = DateTime.Now;
TimeSpan len = cmdEnd - cmdStart;
}
如果我使用以下 sql,第一次迭代需要 0.5 秒。第二个需要 1.1 秒。第三个需要 3.3 秒。依此类推,直到它只是抛出一个超时。
string sql =
"INSERT INTO Test " +
" ([name] " +
" ,[email] " +
" ,[country] " +
" ,[comment] " +
" ,[date] " +
" ,[key_v0] " +
" ,[key_v1] " +
" ,[expires_v1] " +
" ,[expires_v2] " +
" ) " +
" VALUES " +
" (@name " +
" ,@email " +
" ,@country " +
" ,' ' " +
" ,@date " +
" ,@key_v0 " +
" ,@key_v1 " +
" ,@expires_v1 " +
" ,@expires_v2 " +
" )";
但是,如果我使用下面的 sql,整个循环会在一秒钟内执行完毕。
string sql =
"INSERT INTO Test " +
"([name] " +
",[email] " +
",[country] " +
",[comment] " +
",[date] " +
",[key_v0] " +
",[key_v1] " +
",[expires_v1] " +
",[expires_v2] " +
") " +
"VALUES " +
"(@name " +
",@email " +
",@country " +
",' ' " +
",@date " +
",@key_v0 " +
",@key_v1 " +
",@expires_v1 " +
",@expires_v2 " +
")";
唯一的区别是空格。删除空格使总字符数从 428 增加到 203。除了对 4k 和 8k 限制的引用之外,我无法找到任何引用 CommandText 长度的内容。我离那还差得很远。
我已经在运行探查器的情况下运行了两个版本,并且所有调用的持续时间都低于 10 毫秒。延迟似乎是从 SQL 引擎中的命令完成到 ExecuteNonQuery 返回。
我知道有其他方法可以做到这一点。我不是在问更好的方法来做到这一点。我在问减速的根源。
更新: 作为测试,我在查询末尾添加了空格。一旦我的字符总数超过 400 个,它就变慢了。有趣的是,在 414 个字符中,前 99 个插入速度很快。在 415 个字符处,前 9 个插入速度很快。因为我正在根据迭代次数更改一些字符串,所以这有点道理。例如第 10 个刀片比第 9 个刀片长一点,第 100 个刀片比第 99 个刀片长一点。
虽然我有点理解更长的插入应该需要更长的时间,但我无法理解快速和慢速之间的明确划分以及差异的巨大程度。我也不明白为什么花费的时间会增加。
更新 2: (针对 Peter Oehlert 的回答的附加信息): 整个数据库是干净的。没有其他表,并且每次运行都会删除并重新创建测试表。没有索引、触发器或外键。有一个“id”列是主键。
这是从专门为解决此问题而编写的控制台应用程序中提取的代码。它只包含重现此行为的必要代码。
(附加分析器信息): 运行 SQL 分析器时,有一个名为 TextData 的列显示命令和数据是什么。一个例子是这样的:
exec sp_executesql N'INSERT INTO Test ([name] ,[email] ,[country] ,[comment] ,[date] ,[key_v0] ,[key_v1] ,[expires_v1] ,[expires_v2] ) VALUES (@name ,@email ,@country ,'' '' ,@date ,@key_v0 ,@key_v1 ,@expires_v1 ,@expires_v2 ) ',N'@name nvarchar(4),@country nvarchar(2),@email nvarchar(3),@date datetime,@key_v0 nvarchar(4000),@key_v1 nvarchar(4000),@expires_v1 datetime,@expires_v2 datetime',@name=N'9Bob',@country=N'us',@email=N'Jim',@date='2009-08-28 15:35:36.5770000',@key_v0=N'',@key_v1=N'',@expires_v1='2009-08-28 15:35:36.5770000',@expires_v2='2009-08-28 15:35:36.5770000'
那行有 796 个字符,运行速度很快。将名称从“9Bob”更改为“10Bob”会导致插入速度变慢。 796 和 797 似乎都不是一个重要的数字。删除 exec sp_executesql 部分意味着长度为 777 和 778。它们似乎也不重要。
我被难住了。
更新: 在此处发布跟踪:http://www.jere.us/WierdInserts.trc
【问题讨论】:
-
0确保在一种情况下不要将
sql添加到cmd.CommandText,并在另一种情况下替换CommandText。 -
这与您的问题无关,但通常您应该使用 StringBuilder 来创建更长的字符串连接,而不是加号运算符。效率更高。
-
@Adrian Grigore,不一定...因为他正在连接文字字符串,编译器应该能够优化它。
-
如果你将'sql'变量声明为静态只读会发生什么?
-
另外:使用 system.diagnostics.stopwatch 而不是 datetime.now 进行基准测试。
标签: c# sql-server