17

我不知道这是否是我如何使用它们或 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 也是如此(我认为)。

有没有办法让它做到这一点?或者我错过了什么?

4

2 回答 2

25

如果 TVP 比其他选项“明显慢”,那么很可能您没有正确实施它们。

  1. 您不应该使用 DataTable,除非您的应用程序在将值发送到 TVP 之外还使用了它。使用该IEnumerable<SqlDataRecord>接口更快并且使用更少的内存,因为您不会复制内存中的集合只是为了将其发送到数据库。我在以下地方记录了这一点:
  2. 您不应该使用AddWithValueSqlParameter,尽管这不太可能是性能问题。但是,它应该是:

    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. TVP 是表变量,因此不维护统计信息。意思是,他们向查询优化器报告只有 1 行。所以,在你的过程中,要么:
    • 对使用 TVP 的任何查询使用语句级重新编译,而不是简单的 SELECT:OPTION (RECOMPILE)
    • 创建本地临时表(即 single #)并将 TVP 的内容复制到临时表中
    • 您可以尝试将集群主键添加到用户定义的表类型
    • 如果使用 SQL Server 2014 或更新版本,您可以尝试使用 In-Memory OLTP / memory-optimized tables。请参阅:使用内存优化更快的临时表和表变量

关于您看到的原因:

insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )

代替:

insert into @data ( ... fields ... ) 
values ( ... values ... ),
       ( ... values ... ),

如果这实际上是正在发生的事情,那么:

  • 如果插入是在事务中完成的,那么就没有真正的性能差异
  • 较新的值列表语法(即VALUES (row1), (row2), (row3))仅限于 1000 行,因此对于没有该限制的 TVP 来说不是一个可行的选择。但是,这不太可能是使用单个插入的原因,因为这样做时没有限制INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col]),我在此处记录了这一点: 表值构造函数的最大行数。反而...
  • 原因很可能是进行单独插入允许将值从应用程序代码流式传输到 SQL Server:
    1. 使用迭代器(即IEnumerable<SqlDataRecord>上面 #1 中提到的),应用程序代码发送从方法返回的每一行,并且
    2. 构建VALUES (), (), ...列表,即使采用这种INSERT INTO ... SELECT FROM (VALUES ...)方法(不限于 1000 行),仍然需要在将任何数据发送到 SQL Server之前构建整个 VALUES列表。如果有很多数据,那么构建超长字符串会花费更长的时间,并且在构建时会占用更多的内存。

另请参阅 SQL Server 客户咨询团队的这份白皮书:使用 TVP 最大化吞吐量

于 2015-02-24T17:52:36.100 回答
5

请参阅“表值参数与 BULK INSERT 操作”部分
http://msdn.microsoft.com/en-us/library/bb510489.aspx

Quote: “...表值参数在插入少于 1000 行时表现良好。”

它还有一个表格来显示根据插入操作的速度使用什么技术。

我希望这会有所帮助,祝你好运。

于 2011-05-20T12:36:10.660 回答