通过INNER JOIN进行SQL更新?

时间:2011-10-19 06:12:40

标签: sql sql-server tsql sql-server-2008

我必须编写一个查询来根据另外两个不同表中的记录更新表中的所有记录。我写了以下三个查询迭代,我认为第三个是最有效的,第一个是最差的。我只是想得到第二个意见,并找出我是否能比下面的第三个版本做得更好:

P.S:第一个不是真正有效的SQL查询,而是我计划如何查询数据库的伪代码。

SELECT AccountID,Label FROM QueueTable

For each record in query above

    SELECT FeedbackID FROM FeedbackIndexed WHERE FeedbackIndexed.Label = QueueTable.Label
                                       AND  FeedbackIndexed.AccountID = QueueTable.AccountID

    UPDATE FeedbackTable SET Flag = 1 WHERE FeedbackID=@FeedbackID
next


---------------------------------------------------------------------------------------------------------------------

UPDATE FeedbackTable
SET    Flag = 1
WHERE  FeedbackID IN(SELECT DISTINCT FeedbackID
                           FROM   FeedbackIndexed,
                                  QueueTable
                           WHERE  FeedbackIndexed.Label = QueueTable.Label
                                  AND FeedbackIndexed.AccountID = QueueTable.AccountID)



----------------------------------------------------------------------------------------------------------------------


UPDATE FeedbackTable 
SET    FeedbackTable.Flag = 1
FROM   FeedbackTable
       INNER JOIN FeedbackIndexed
         ON FeedbackIndexed.FeedbackID = FeedbackTable.FeedbackID
       INNER JOIN QueueTable WITH (TABLOCK)
         ON FeedbackIndexed.Label = QueueTable.Label
            AND FeedbackIndexed.AccountID = QueueTable.AccountID


(I used table lock on QueueTable because at the end of this query i want to drop all records from the que and don't want other parts of the app adding more records to this table while the query above runs, is that right way to do this?)

1 个答案:

答案 0 :(得分:5)

你的第二个和第三个例子都是有效的。以下是几点:

  1. 在您的第二个查询中,不必使用DISTINCT只会增加开销。执行IN操作时,SQL通常不会执行完整的连接操作,并在找到匹配后立即退出。它也不返回所有行,只返回true / false是否匹配给定值。
  2. 在第二个示例中使用IN可能会产生一个更优化的连接运算符(半连接与连接),因为您明确声明您对子查询的输出不感兴趣,只是是否存在是返回的记录。
  3. 您最好使用EXISTS子句。虽然常见的误解是IN效率低于EXISTS(它们实际上在大多数情况下实现相同的查询)IN在处理空值时可能会产生意外结果。
  4. EXISTS版本看起来像这样:

    UPDATE FT
    SET    Flag = 1
    FROM   FeedbackTable
    WHERE  EXISTS(SELECT * 
            FROM FeedbackIndexed FI 
                INNER JOIN QueueTable QT 
                ON FI.Label = QT.Label 
                    AND FI.AccountID = QT.AccountID
              WHERE FeedbackID = FT.feedbackID)
    

    基础查询计划可能与您的IN示例完全相同(删除冗余DISTINCT后),它可能会产生与第3个示例相同的查询计划,但总是很好知道解决问题的不同方法。

    还有几点。

    • 您的TABLOCK将在查询完成时释放,除非您将查询和查询包装在显式事务中删除已处理的记录。我非常确定你也想在这里添加HOLDLOCKHOLDLOCK将在交易期间持有锁。
    • 您提到了阻止写入队列表的问题,读取怎么办?如果您的使用者proc同时运行多个实例,TABLOCK将实现共享锁,这可能会导致竞争条件。如果这是一个问题,请考虑使用TABLOCKX
    • 可能有更好的方法来管理那里的锁定和竞争条件。我喜欢使用SP_GETAPPLOCK。几年前我写了一篇关于这个主题的文章,可能值得一读sp_getapplock

    我希望这会有所帮助。