如何通过避免循环来优化这个t-sql脚本代码?

时间:2016-03-14 11:33:33

标签: sql sql-server database tsql

我使用以下sql查询来更新MyTable。代码需要5到15分钟。只要ROWS< = 100000000但是当Rows>时更新MyTabel 100000000需要指数时间才能更新MYTable。如何更改此代码以使用set-base而不是while循环?

VISITOR No.: <?php the_widget( "ADS-WpSiteCount" ); ?> 

2 个答案:

答案 0 :(得分:1)

这将消除循环

UPDATE MyTable
   set DoorsReleased = ~DoorsReleased
 WHERE LitraID = 8175
   AND id BETWEEN 100000000 AND 300000000 
   AND DoorsReleased is not null -- if DoorsReleased is nullable
-- AND DoorsReleased <> ~DoorsReleased</strike>

如果您设置循环
以下工作
我以为〜是列名的一部分,但它不是运算符

select 1;
WHILE (@@ROWCOUNT > 0)
BEGIN
    UPDATE top (100000) MyTable
       set DoorsReleased = ~DoorsReleased
     WHERE LitraID = 8175
       AND id BETWEEN 100000000 AND 300000000 
       AND (       DoorsReleased <> ~DoorsReleased 
             or (  DoorsReleased is null and ~DoorsReleased is not null )
           )
END

在事务内部我不认为循环会有价值,因为事务日志无法清除。 10,000的批量很小。\

如评论中所述,如果你想循环然后尝试使用id作为row_number()所有这些循环是昂贵的

您可以使用OFFSET

答案 1 :(得分:1)

您的一个问题是循环中的select语句获取LitraID = 8175的所有记录,设置行号,然后在update语句中过滤。每次迭代都会发生这种情况。

这样做的一种方法是在进入循环之前获取更新的所有ID并将它们存储在临时表中。然后你可以写一个类似的查询到你拥有的那个,但加入这个id表。

但是,如果您知道有多少记录具有LitraID = 8175以及它们是否遍布整个表格,而不是与类似的ID捆绑在一起,那么还有一种更简单的方法。

DECLARE @batchSize INT
DECLARE @minId INT
DECLARE @maxId INT

SET @batchSize = 10000 --adjust according to how frequently LitraID = 8175, larger numbers if infrequent
SET @minId = 100000000

WHILE @minId <= 300000000 BEGIN

    SET @maxId = @minId + @batchSize - 1
    IF @maxId > 300000000 BEGIN
        SET @maxId = 300000000
    END

    BEGIN TRANSACTION T

        UPDATE MyTable
        SET DoorsReleased = ~DoorsReleased
        WHERE id BETWEEN @minId AND @maxId

    COMMIT TRANSACTION T

    SET @minId = @maxId + 1
END

这将使用id的值来控制循环,这意味着您不需要额外的步骤来计算@iterationCount。它使用小批量,因此桌子不会长时间锁定。它没有任何不必要的SELECT语句,并且假设id具有索引,更新中的WHERE子句是有效的。

它不会在每笔交易中更新完全相同的记录数量,但没有理由这样做。