【问题标题】:Is a cursor the only alternative to do this kind of operation游标是做这种操作的唯一选择吗
【发布时间】:2011-09-21 15:22:28
【问题描述】:

我正在尝试优化一个长事务,我已经看到以下操作已经完成了很多次:

Declare @myCursor CURSOR FAST_FORWARD FOR
SELECT field1, MIN(COALESCE(field2, -2)) FROM MyTable tempfact
LEFT JOIN MyTable sd
ON tempfact.ID = sd.ID AND sd.TransactionId = @transactionId
WHERE tempfact.SomeField IS NULL
AND tempfact.TransactionId = @transactionId
GROUP BY tempfact.field1

OPEN @myCursor

FETCH NEXT FROM @myCursor INTO @field1Variable, @field2Variable

WHILE @@FETCH_STATUS = 0
BEGIN
   EXEC USP_SOME_PROC @field1Variable, @field2Variable
   FETCH NEXT FROM @myCursor INTO @field1Variable, @field2Variable
END

CLOSE @myCursor
DEALLOCATE @myCursor

USP_SOME_PROC sproc 的代码如下:

IF NOT EXISTS (SELECT * FROM SomeTable WHERE Field1 = @field1)
BEGIN
   INSERT INTO SomeTable (Field1, Field2)
   VALUES (@field1, @field2)
END

就像我提到的,这是在很多地方完成的,涉及的表和字段不同,但想法保持不变,我相信如果没有游标,可能有一种方法可以提高这些存储过程的性能使用并且可能通过使这个事务更快,我们遇到的死锁问题(另一个帖子的主题)可能会得到解决。

【问题讨论】:

  • 100 次中有 99 次,游标逻辑可以转换为基于集合的逻辑。

标签: sql-server sql-server-2008 database-cursor


【解决方案1】:

您可以为此使用MERGE

;WITH Source AS
(
SELECT field1,
       MIN(COALESCE(field2, -2)) as field2
FROM   MyTable tempfact
       LEFT JOIN MyTable sd
         ON tempfact.ID = sd.ID
            AND sd.TransactionId = @transactionId
WHERE  tempfact.SomeField IS NULL
       AND tempfact.TransactionId = @transactionId
GROUP  BY tempfact.field1  
)
MERGE SomeTable AS T
USING Source S
ON (T.Field1 = S.Field1)


WHEN NOT MATCHED BY TARGET THEN
    INSERT (Field1, Field2)
    VALUES (field1, field2)
    ;

【讨论】:

    【解决方案2】:

    我还没有机会对此进行测试,但这应该很接近:您需要从 SELECT 语句中插入,但还需要确保 SomeTable 中不存在相应的记录

    INSERT INTO SomeTable (Field1, Field2)
    SELECT field1, MIN(COALESCE(field2, -2)) 
    FROM MyTable tempfact 
        LEFT JOIN MyTable sd ON tempfact.ID = sd.ID AND sd.TransactionId = @transactionId 
        LEFT JOIN SomeTable st ON st.Field1 = tempfact.field1
    WHERE tempfact.SomeField IS NULL 
        AND tempfact.TransactionId = @transactionId 
        AND st.Field1 IS NULL
    GROUP BY tempfact.field1 
    

    【讨论】:

    • 在默认read committed隔离级别下,这里可能会出现竞争条件。
    • 嗨,Martin,您能否详细说明或发布一些关于您在此处评论的内容的链接?
    • @Sergio-SELECT 语句只会获取共享锁,然后在读取数据后立即释放它们,因此另一个事务可以插入具有相同 @987654325 之一的行select 之后的@values 已经得出该行不存在的结论。因此,您最终会得到两行具有相同 field1 值的行(如果强制执行唯一约束,则会出现错误)
    • @Martin- 非常感谢。我发现这个博客虽然看起来合并也可能导致竞争条件,但它更容易防止并且在这种情况下它的行为确实比 SELECT 更好。 link
    • @Sergio - 谢谢。这很有趣。我一直认为使用MERGE 会自动处理问题。
    【解决方案3】:

    你不需要游标,可以使用如下的批量插入逻辑

    INSERT INTO SomeTable (Field1, Field2) 
    SELECT 
           field1,       
           MIN(COALESCE(field2, -2)) 
    FROM
          MyTable tempfact        
    LEFT JOIN 
          MyTable sd          
               ON tempfact.ID = sd.ID             
                  AND sd.TransactionId = @transactionId 
    WHERE  
          tempfact.SomeField IS NULL        
          AND tempfact.TransactionId = @transactionId 
    GROUP  BY 
          tempfact.field1
    

    希望这会有所帮助!

    【讨论】:

    • 一开始我也是这么想的,但你似乎也跳过了IF NOT EXISTS检查程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多