Змусити SQL Server виконувати умови запиту, як написано?


14

Я використовую SQL Server 2008 R2, і у мене є цей псевдо-запит (SP):

select ...
from ...
WHERE    @LinkMode IS NULL
     AND (myColumn IN (...very long-running query...))
     ...
     ...

Проблема полягає в тому, що запит займає дуже багато часу, навіть якщо я виконую SP @LinkMode=2.

Як ви помітили, тривалий запит повинен виконуватися лише в тому випадку, якщо @LinkMode є нульовим, що тут не так. У моєму випадку @LinkMode = 2!

Однак якщо я зміню це на:

 select ...
    from ...
    WHERE    1=2
         AND (myColumn IN (...very long time exeted query...))
     ...
     ...

СП дійсно працювати швидко.

Я чув, що раніше оптимізатор може оптимізувати порядок критеріїв.

Тому я прошу:

  • Навіть якщо оптимізатор вибере інший маршрут, що може бути швидше, ніж перевірка, якщо =null? Я маю в виду, я думаю , що перевірка if a==nullє набагато швидше , ніж при запуску іншого довгого запиту ...

  • Як я можу змусити SQL Server виконувати запит так, як я його написав (те саме замовлення)?

Відповіді:


22

Ви потрапляєте у пастку " Catch-All Query ", що тут дуже добре пояснює Гейл Шоу .

Для узагальнення проблеми: SQL Server оптимізує значні накладні витрати на компіляцію запитів, кешуючи план запитів після компіляції, а потім перевіряючи кеш на відповідність плану запитів перед наступною компіляцією. "Збіг", який відбувається тут, є чисто текстовим, тому фактичне значення змінної на це не вплине.

Це добре 99% часу, але в деяких випадках це погано . Один випадок, коли це погано, коли хтось намагається побудувати пункт WHERE так, ніби це схоже на короткий замикання оператора IF в C, і т. Д. Це не працює добре, оскільки компілятор SQL повинен скласти один план запитів, який буде працювати незалежно. того, якими є насправді значення параметрів, і єдиним способом, яким він може керувати цими "розумними" логічними умовами перемикання в пункті WHERE, є створення простого плану грубої сили, який просто сканує всю таблицю, фільтруючи рядки, як йде , без використання жодних індексів.

Не дивно, що це робить їх рівномірно повільними, незалежно від значення параметрів / змінних.


8

Немає гарантованого способу змусити SQL-сервер виконувати умови пункту в певній послідовності. Оптимізатор завжди оцінить їх у порядку, який вважає за потрібне.

Що ви можете зробити, це щось подібне:

IF @LinkMode IS NULL
BEGIN
    select ...
    from ...
    WHERE (myColumn IN (...very long time exeted query...))
         ...
         ...
END
ELSE
BEGIN
    select ...
    from ...
    WHERE ...
         ...
END

3

Якщо це варіант, використовуйте оператор IF для виконання відповідної форми запиту. Крім того, в SQL ви кажете db-двигуну, що робити, а не як це робити - від початку до кінця все не виконується. Може бути важко передбачити, що саме це зробить. Ви, напевно, це знаєте;)


2

Динамічний SQL, ймовірно, також працюватиме, оскільки в цьому випадку оптимізатор запитів повинен отримати фактичні значення під час виконання (виправте мене, якщо я помиляюся, я насправді не впевнений, але, схоже, пам’ятаю, що використовую його для подібних ситуацій) . Але я з іншими в цьому, в тому, що пункт IF / ELSE буде служити вам найкраще, оскільки це найпростіше і найпростіше рішення, яке зробить саме те, що потрібно.

Для подальшої посилання у випадку, якщо ви його ще не використовували, тут можна знайти жахливий некрасивий сайт із робочим прикладом для динамічного SQL: http://sqlusa.com/bestpractices/dynamicsql/


1

Я б рекомендував конструкцію IF / ELSE. Якщо з будь-якої причини, яка не працює для вас, ви завжди можете розглянути можливість використання параметра REC RECOMPILE ..


Не могли б ви детальніше розглянути, як може виглядати конструкція "якщо / ще"? : D
jcolebrand

Я збирався запропонувати використовувати ОПЦІЮ (З РЕКОМПЛІЄЮ), оскільки це створюватиме ідеальний план кожного разу - затримка компіляції додасть накладні витрати, але я підозрюю, що в цьому випадку в цілому краще.
SqlRyan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.