Будет ли SQL Server достаточно умным, чтобы не выполнять дорогостоящие запросы, если в этом нет необходимости? (короткое замыкание)

Таким образом, в SQL Server нет прямого замыкания, как, например, в операторах if в языках программирования общего назначения.

Итак, рассмотрим следующий макет запроса:

SELECT * FROM someTable
WHERE name = 'someValue' OR name in (*some extremely expensive nested sub-query only needed to cover 0.01% of cases*)

Допустим, в таблице всего 3 строки, и все они соответствуют name = 'someValue'. Будет ли когда-либо выполняться дорогостоящий подзапрос? Допустим, есть 3 миллиона строк, и все, кроме 1, могут быть получены с именем = 'someValue', кроме 1 строки, которую необходимо получить с помощью подзапроса. Будет ли подзапрос когда-либо оцениваться, когда он не нужен?

Если у кого-то есть аналогичный реальный случай, можно позволить 0,01% дождаться выполнения дорогостоящего подзапроса, прежде чем получать результаты, если результаты извлекаются быстро без подзапроса для 99,99% случаев. (Я знаю, что мой конкретный пример выше может быть явно обработан с помощью операторов IF в SP, как это предлагается в этой связанной теме: Короткое замыкание Sql ИЛИ или условное существует в предложении where, но давайте предположим, что это не вариант.)


person RoleyBaxter    schedule 23.04.2020    source источник
comment
Булевы операторы SQL не гарантируют короткого замыкания и не гарантируют, что этого не произойдет. Оптимизатор может опускать или не опускать предложения в зависимости от того, как закончится план выполнения, что, в свою очередь, зависит от множества других факторов, поэтому простого ответа нет. В принципе, что касается семантики, вы должны исходить из того, что они не замыкаются. Использование CASE более надежно, чем логические операторы, но не гарантирует 100% отсутствие ненужных предложений (но все же гораздо более последовательно). Если вам нужна производительность, это, вероятно, лучший вариант.   -  person Jeroen Mostert    schedule 23.04.2020
comment
Вы можете возразить, что подзапрос не нужен, когда все строки равны someValue, но даже если нет, он необходим. Если вы говорите, что у вас будет по крайней мере одна строка, не равная someValue, тогда да, ее нужно будет выполнить, без этого не обойтись. Однако, скорее всего, он будет выполнен, когда все строки равны someValue. Если он должен оставаться одним запросом, сделайте два запроса, для = 'someValue' и для <> 'someValue' and (expensive), и union all результаты в один. Хотя это все равно не гарантирует.   -  person GSerg    schedule 23.04.2020
comment
FWIW, я запустил код в вашем вопросе с подзапросом, ссылающимся на большую таблицу без полезного индекса. В большую таблицу никогда не вносились 3 строки 'someValue', потому что план включал фильтр выражения запуска для короткого замыкания. YMMV зависит от многих факторов, поэтому поведение нельзя обобщать.   -  person Dan Guzman    schedule 23.04.2020
comment
@DanGuzman Спасибо всем за комментарии, очень интересно. В моем реальном случае дорогой подзапрос был не таким уж дорогим, поэтому я просто пошел по более простому пути, поскольку потеря производительности была незначительной, но это разожгло мое любопытство. Хорошо иметь в виду, что не всегда доверять плану выполнения, чтобы решить его наилучшим образом, я думаю   -  person RoleyBaxter    schedule 23.04.2020
comment
Если план выполнения недостаточно умен, чтобы его разработать, может помочь создание индексов, статистики или ограничений, чтобы дать ему достаточно подсказок.   -  person Nick.McDermaid    schedule 23.04.2020


Ответы (1)


Как отмечают комментарии, оптимизатор в SQL Server довольно умен.

Вы можете попытаться выполнить короткое замыкание, используя case. Поскольку документация гласит:

Выражение CASE последовательно оценивает свои условия и останавливается на первом условии, чье условие выполнено.

Обратите внимание, что есть некоторые исключения, связанные с агрегированием. Итак, вы можете сделать:

SELECT t.*
FROM someTable t
WHERE 'true' = (CASE WHEN t.name = 'someValue' THEN 'true'
                     WHEN t.name in (*some extremely expensive nested sub-query only needed to cover 0.01% of cases*)
                     THEN 'true'
                END)

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

В вашем случае я подозреваю, что замена IN на EXISTS и использование соответствующих индексов может устранить почти все потери производительности подзапроса. Однако это другое дело.

person Gordon Linoff    schedule 23.04.2020