ОБНОВЛЕНИЕ SQL через ВНУТРЕННЕЕ СОЕДИНЕНИЕ? - PullRequest
1 голос
/ 19 октября 2011

Мне пришлось написать запрос, чтобы обновить все записи в таблице на основе записей, которые существуют в двух других разных таблицах. Я написал следующие три итерации запроса, я думаю, что третья - самая эффективная, а первая - худшая. Я просто хотел получить второе мнение и выяснить, смогу ли я сделать лучше, чем третья версия ниже:

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 Ответ

5 голосов
/ 19 октября 2011

Ваш второй и третий примеры верны. Вот несколько моментов:

  1. Во втором запросе необязательно использовать DISTINCT, который просто добавит накладные расходы. Когда вы выполняете операцию IN, SQL, как правило, не выполняет операцию полного соединения и завершает работу, как только найдено совпадение. Он также не возвращает все строки, просто true / false, если есть совпадение для данного значения.
  2. Использование IN во втором примере может привести к более оптимальному оператору соединения (semi-join vs join), потому что вы явно заявляете, что вас не интересует вывод из подзапроса, просто есть или нет записи вернулся.
  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)

Базовый план запроса, вероятно, будет точно таким же, как ваш IN пример (после удаления избыточного DISTINCT), и он может дать тот же план запроса, что и в третьем примере, но всегда полезно знать различные подходы к решение проблемы.

Еще несколько очков.

  • Ваш TABLOCK будет выпущен, когда запрос будет завершен, если вы не заключите запрос и запрос на удаление обработанных записей в явной транзакции. Я почти уверен вы тоже захотите добавить HOLDLOCK здесь. HOLDLOCK будет удерживать блокировку на протяжении транзакции.
  • Вы упомянули проблему предотвращения записи в вашу таблицу очередей, а как насчет чтения? TABLOCK будет реализовывать общую блокировку, которая может вызвать состояние гонки, если ваш потребительский процесс запускает несколько экземпляров одновременно. Попробуйте использовать TABLOCKX, если это будет проблемой.
  • Там могут быть лучшие способы управления блокировками и условиями гонки. Мне нравится использовать SP_GETAPPLOCK для этого. Я написал статью на эту тему несколько лет назад, возможно, стоит прочитать sp_getapplock

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...