表值参数性能问题
我不知道这是否是我使用它们的方式或 Microsoft 实现的问题,但 SQL 2008 表值参数非常慢。
一般来说,如果我需要使用 TVP,那是因为我有很多记录 - 目前,对于超过最少记录的任何内容,它们似乎都慢得无法使用。
我在 .Net 中这样调用它们:
// get the data
DataTable data = GetData();
com.CommandText = "sprocName"
// create the table-value parameter
var tvp = com.Parameters.AddWithValue("data", data);
tvp.SqlDbType = SqlDbType.Structured;
com.ExecuteNonQuery();
我运行探查器来查看原因,实际的 SQL 语句是这样的:
declare @data table ...
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
sprocName(@data)
不过,这是一种非常慢的方法。 如果这样做的话会快得多:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
-- for each row
( ... values ... )
我不确定为什么它不使用更新、更快的语法。或者甚至是它在 SqlBulkCopy 下所做的任何事情。
SQL 2008 中添加了新语法,但 TVP 也是如此(我认为)。
有什么选择可以让它做到这一点吗?或者我缺少什么?
I don't know whether this is an issue with how I'm using them or Microsoft's implementation, but SQL 2008 table-value parameters are painfully slow.
Generally if I need to use a TVP it's because I've got lots of records - currently they appear to be unusably slow for anything more than the fewest records.
I'm calling them in .Net like this:
// get the data
DataTable data = GetData();
com.CommandText = "sprocName"
// create the table-value parameter
var tvp = com.Parameters.AddWithValue("data", data);
tvp.SqlDbType = SqlDbType.Structured;
com.ExecuteNonQuery();
I ran profiler to see why, and the actual SQL statement is something like this:
declare @data table ...
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
sprocName(@data)
That's a really slow way to do it though.
It would be much quicker if it did this instead:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
-- for each row
( ... values ... )
I'm not sure why it isn't using the newer, much quicker syntax. Or even whatever it does under the hood with SqlBulkCopy
.
The new syntax was added in SQL 2008, but then so are TVPs (I think).
Is there some option to make it do this? Or something that I'm missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果 TVP 比其他选项“明显慢”,那么很可能您没有正确实施它们。
IEnumerable
接口速度更快,并且使用更少的内存,因为您不必在内存中复制集合只是为了将其发送到数据库。我已将其记录在以下位置:您不应该对 SqlParameter 使用
AddWithValue
,尽管这不太可能是性能问题。但仍然应该是:OPTION (RECOMPILE)
#
)并将TVP的内容复制到临时表中关于您所看到的原因:
而不是:
如果这实际上正在发生,那么:
VALUES (row1), (row2), (row3)
)仅限于 1000 行左右,因此不是一个可行的选项对于没有该限制的 TVP。然而,这不太可能是使用单个插入的原因,因为在执行 INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), .. 时没有限制。 .) tab([col]),我在这里记录:最大行数表值构造函数。相反...
IEnumerable
),应用代码会发送从方法返回的每一行,并且VALUES (), (), ...
列表,即使使用INSERT INTO ... SELECT FROM (VALUES ...)
方法 (不限于 1000 行),在将任何数据发送到 SQL Server 之前,仍然需要构建整个VALUES
列表。如果数据量很大,构造超长字符串的时间会更长,而且会占用更多的内存。另请参阅 SQL Server 客户咨询团队提供的白皮书:利用 TVP 最大化吞吐量
If TVPs are "noticeably slower" than the other options, then most likely you are not implementing them correctly.
IEnumerable<SqlDataRecord>
interface is faster and uses less memory as you are not duplicating the collection in memory only to send it to the DB. I have this documented in the following places:You should not use
AddWithValue
for the SqlParameter, though this is not likely a performance issue. But still, it should be:OPTION (RECOMPILE)
#
) and copy the contents of the TVP into the temp tableRegarding why you are seeing:
instead of:
IF that is actually what is happening, then:
VALUES (row1), (row2), (row3)
) is limited to something like 1000 rows and hence not a viable option for TVPs that do not have that limit. HOWEVER, this is not likely the reason that individual inserts are being used, given that there is no limit when doingINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, which I documented here:Maximum Number of Rows for the Table Value Constructor. Instead...
IEnumerable<SqlDataRecord>
noted in #1 above), the app code sends each row as it is returned from the method, andVALUES (), (), ...
list, even if doing theINSERT INTO ... SELECT FROM (VALUES ...)
approach (which is not limited to 1000 rows), that would still require building the entireVALUES
list before sending any of the data into SQL Server. If there is a lot of data, that would take longer to construct the super-long string, and it would take up a lot more memory while doing it.Please also see this whitepaper from the SQL Server Customer Advisory Team: Maximizing Throughput with TVP
请参阅“表值参数与 BULK INSERT 操作”部分
http://msdn.microsoft.com/en-us/library/bb510489.aspx
Quote: “...表值参数在插入少于 1000 行时表现良好。”
它还具有一个表格,显示根据插入操作的速度使用哪些技术。
我希望这有帮助,祝你好运。
See the section "Table-Valued Parameters vs. BULK INSERT Operations"
http://msdn.microsoft.com/en-us/library/bb510489.aspx
Quote: "... table-valued parameters perform well for inserting less than 1000 rows."
It also has a table to show what technology to use based on the speed of insert operations.
I hope this helps, good luck.