Помогает ли создание CTE в этом случае?

#sql-server-2008

#sql-server-2008

Вопрос:

У меня есть запрос, написанный очень плохо в SQL Server 2008

 UPDATE  PatientChartImages
SET     PatientChartImages.IsLockDown = @IsLockdown
WHERE   PatientChartImages.IsLockDown = @IsNotLockdown
    AND PatientChartId IN (
    SELECT  PatientCharts.PatientChartId
    FROM    PatientCharts
    WHERE   ( PatientCharts.ChartStatusID = @ChartCompletedStatusID
              OR PatientCharts.ChartStatusID = @ChartOnBaseStatusID
            )
            AND PatientCharts.IsLockDown = @IsNotLockdown
            AND PatientCharts.CompletedOn IS NOT NULL
            AND DATEDIFF(MINUTE, PatientCharts.CompletedOn, GETUTCDATE()) >= ( SELECT
                                                          tf.LockUpInterval
                                                          FROM
                                                          @tblFacCOnf tf
                                                          WHERE
                                                          tf.facilityId = PatientCharts.FacilityId
                                                          ) )
  

Этот запрос блокирует основную таблицу и приводит к таймауту. ЕСЛИ я создам CTE сначала все обновляемые записи, а затем обновлю основную таблицу, присоединившись к CTE. Поможет ли это??

Комментарии:

1. Можете ли вы опубликовать план выполнения, сгенерированный этим запросом? На первый взгляд, последним WHERE условием ( AND DATEDIFF(.. ) является принудительное сканирование таблицы.

Ответ №1:

Первое, что я советую вам сделать, это заменить IN condition на EXISTS . Второе — перенести всю эту условную логику в CTE. Третье — заменить подвыбор на @tblFacCOnf на join.

Последний совет зависит от вашей бизнес-логики и, на мой взгляд, не так важен

Итак, в конце вы получите что-то вроде

 WITH search_cte as (
  SELECT  PatientCharts.PatientChartId
  FROM    PatientCharts 
  JOIN @tblFacCOnf tf on tf.facilityId = PatientCharts.FacilityId
  WHERE   ( PatientCharts.ChartStatusID = @ChartCompletedStatusID
          OR PatientCharts.ChartStatusID = @ChartOnBaseStatusID
        )
        AND PatientCharts.IsLockDown = @IsNotLockdown
        AND PatientCharts.CompletedOn IS NOT NULL
        AND DATEDIFF(MINUTE, PatientCharts.CompletedOn, GETUTCDATE()) >= tf.LockUpInterval
) --cte end                                               
UPDATE  PatientChartImages
SET     PatientChartImages.IsLockDown = @IsLockdown
WHERE   PatientChartImages.IsLockDown = @IsNotLockdown
AND EXISTS (select 1 from PatientChartImages where PatientChartImages.PatientChartId = search_cte.PatientChartId)
  

Ответ №2:

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

Ответ №3:

Блокировка обновления удерживается в течение времени, необходимого для вычисления CTE и времени обновления. Время CTE, вероятно, вызывает тайм-аут.

Сократить время блокировки до минимума, необходимого для обновления целевой таблицы. Я предлагаю вам создать временную таблицу с двумя столбцами. Col1 — это первичный ключ или ключ кластера целевой таблицы, а Col2 — это значение, которое вы хотите внести в целевую таблицу. Завершите создание временной таблицы и заполните таблицу значениями в соответствии с вашей бизнес-логикой в рамках транзакции. Обновите целевую таблицу, используя соединение с временной таблицей и значение из временной таблицы в отдельной транзакции. После обновления удалите временную таблицу.

Ответ №4:

Я думаю, вам следует создать SQL-скрипт (или хранимую процедуру, если вы будете использовать его с более высокого уровня), где вы сохраняете результаты вашего выбора в курсоре (вам нужно будет только найти PatientCartId строк, которые будут обновлены), а затем вы должны использовать его в своем обновлении, так что ответ — да.

Это легко проверить, вы должны поместить эти команды в транзакцию, выполнить откат транзакции и перед откатом выполнить выбор для проверки ваших результатов. Удачи.