通过INNER JOIN进行SQL更新?

new*_*bie 1 sql t-sql sql-server sql-server-2008

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

PS:第一个不是真正有效的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?)
Run Code Online (Sandbox Code Playgroud)

Cod*_*ian 5

您的第二个和第三个示例都是有效的.以下是几点:

  1. 在第二个查询中,不需要使用DISTINCT 它只会增加开销.执行IN操作时,SQL通常不会执行完整的连接操作,并在找到匹配后立即退出.它也不返回所有行,只返回true/false是否匹配给定值.
  2. 使用IN你的第二个例子可能会产生更优化的连接符(半连接VS加入),因为你明确指出,你是不是有意从子查询的输出,只是无论是否有记录返回.
  3. 你可能最好使用这个EXISTS条款.虽然这是一种常见的误解,即在处理空值时,IN效率低于EXISTS (它们实际上在大多数情况下实现查询的效率相同)IN会产生意外结果.

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)
Run Code Online (Sandbox Code Playgroud)

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

还有几点.

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

我希望这有帮助.