我不知道这是我使用它们的方式还是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也是如此(我认为)。

是否有使其执行此操作的选项?还是我想念的东西?

最佳答案

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


除非在将值发送到TVP之外您的应用程序已经使用了DataTable,否则您不应使用DataTable。使用IEnumerable<SqlDataRecord>接口更快,并且使用更少的内存,因为您不是在内存中复制集合,而只是将其发送到DB。我在以下地方对此进行了记录:


How can I insert 10 million records in the shortest time possible?(许多额外的信息和链接也位于此处)
Pass Dictionary to Stored Procedure T-SQL
Streaming Data Into SQL Server 2008 From an Application(在SQLServerCentral.com上;需要免费注册)

您不应该对SqlParameter使用AddWithValue,尽管这不太可能导致性能问题。但仍然应该是:

SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);

TVP是表变量,因此不维护统计信息。这意味着,他们仅向查询优化器报告只有1行。因此,在您的过程中,要么:


对于使用TVP的任何查询,除了简单的SELECT以外,对其他查询都使用语句级重新编译:OPTION (RECOMPILE)
创建一个本地临时表(即单个#)并将TVP的内容复制到临时表中
您可以尝试将群集的主键添加到用户定义的表类型
如果使用SQL Server 2014或更高版本,则可以尝试使用内存中OLTP /内存优化表。请参阅:Faster temp table and table variable by using memory optimization



关于您为什么看到:

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])时没有限制,这不太可能是使用单个插入的原因,我在此处记录了该信息:
Maximum Number of Rows for the Table Value Constructor。代替...
原因很可能是通过执行单独的插入操作将值从应用程序代码流式传输到SQL Server:


使用迭代器(即上面#1中提到的IEnumerable<SqlDataRecord>),应用程序代码发送从方法返回的每一行,并且
构造VALUES (), (), ...列表,即使执行INSERT INTO ... SELECT FROM (VALUES ...)方法(不限于1000行),在将任何数据发送到SQL Server之前,仍然需要构建整个VALUES列表。如果有大量数据,则构造超长字符串将花费更长的时间,并且这样做会占用更多的内存。



另请参见SQL Server客户咨询团队的以下白皮书:Maximizing Throughput with TVP

关于.net - 表值参数性能问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6071818/

10-11 01:51