2017-01-25 2 views
0

Рассмотрим эту установку:SQL Server: функция приоритета и короткий curcuiting в котором пункт

create table #test (val varchar(10)) 
insert into #test values ('20100101'), ('1') 

Теперь, если я выполнить этот запрос

select * 
from #test 
where ISDATE(val) = 1 
    and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' 

он потерпит неудачу с

конверсии не удалось при преобразовании даты и/или времени из строки символов

, который сообщает мне, что условия where не закорочены и обе функции оцениваются. ОК.

Однако, если я бегу

select * 
from #test 
where LEN(val) > 2 
    and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' 

он не подведет, который говорит мне, что where положение закорочен в этом случае.

Этот

select * 
from #test 
where ISDATE(val) = 1 
    and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' 
    and LEN(val) > 2 

снова не удастся, но если я перееду проверку длины к перед тем бросить, это работает. Так выглядит, как функции оцениваются в том порядке, в котором они появляются в запросе.

Может кто-нибудь объяснить, почему первый запрос не удается?

ответ

2

Не удается, так как SQL является декларативным, поэтому порядок ваших условий не учитывается при создании плана (и он не требуется для этого).

Обычный способ обойти это использовать CASE, который имеет строгие правила о последовательности и когда остановиться.

В вашем случае вы, вероятно, нужны вложенные CASE с, что-то вроде этого:

WHERE 
    (
     case when ISDATE(val) = 1 then 
      case when CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' and 
        LEN(val) > 2 
        THEN 1 ELSE 0 END 
     ELSE 0 
     END 
    ) = 1 

(обратите внимание, что это вряд ли будет на самом деле правильно SQL, как я только что ввел его в).

Кстати, даже если вы получите «работу», изменив условия, я бы посоветовал вам этого не делать. Примите, что SQL просто не работает таким образом. По мере изменения данных & статистика изменяется, SQL обновляется, рабочая нагрузка меняется, индексы добавляются, план запроса может измениться. Любые попытки «заставить его работать» будут в лучшем случае недолговечными, так что пойдите с CASE, который будет продолжать работать, как только вы это исправите (при условии, что вы гнездо CASE операторов там, где это необходимо, и не попадаете в то же самое приоритет в условиях CASE!)

+0

Я должен был посмотреть на план выполнения – user227895

1

На тайну приходит ответ, если вы изучите План выполнения. Как часть CAST(), так и LEN() применяются как часть этапа сканирования таблицы, тогда как тест для IsDate() представляет собой отдельный тест фильтра после сканирования таблицы.

Похоже, что внутренние оптимизации SQL Engine используют определенные функции фильтрации как часть поиска данных, а другие как отдельные фильтры, почти наверняка, как форму оптимизации запросов, чтобы минимизировать нагрузку с диска в основную память. Однако более сложные функции, такие как IsDate(), которые зависят от системных переменных, таких как системный формат даты в некоторых случаях (это «01/02/2017» 2 января или 1 февраля?), Должны иметь данные, полученные до фильтра применены.

Хотя у меня нет твердой информации об этом, я сильно подозреваю, что любой фильтр, более ресурсоемкий, чем определенный уровень, делегируется шагам фильтра в плане запроса и все, что просто/достаточно быстро, чтобы проверить, как данные считывание выполняется во время шагов сканирования/поиска. Кроме того, если фильтр может быть применен к данным в индексе, я уверен, что он будет протестирован до того, как будут протестированы любые данные, отличные от индекса, исключительно для сведения к минимуму чтения дисков, которые являются плохими показателями производительности juju (это может не относиться к Кластерный индекс таблицы). В этих случаях короткое замыкание может быть не простым, с тестом IsDate(), указанным в неиндексном поле, которое выполняется после аналогичного теста в индексированном поле, независимо от того, где они находятся в списке условий.

При этом, как представляется, верно, что условия короткого замыкания, когда они выполняются на одном и том же этапе плана запроса. Если вы введете строку, такую ​​как '201612123', в таблицу temp, добавьте отметку Len(val) < 9после сравнения даты, она по-прежнему генерирует ошибку, вместо того чтобы одновременно проверять как LEN() условия в крошечной оптимизации.

0

, который сообщает мне, что условия не закорачиваются и обе функции оцениваются.

Чтобы расширить на LoztInSpace's ответ, ваша терминология предполагает, что вы не интерпретируете SQL правильно, на своих условиях.

Различные части заявления SELECT не являются «функциями». Весь оператор атомный. Вы отправляете запрос как единицу, и СУБД отвечает. Нет «до» и «после». Существует только запрос.

Это правила. Ваша задача при формулировании запроса состоит в том, чтобы предоставить тот, который действителен. Это логичная прогрессия: действительный вопрос, действительный ответ и т. Д. В тот момент, когда вы выходите из этой рамки, вы также можете спросить: «Почему небо семь?».

Одно небольшое разъяснение для ответа @ LoztInSpace. Когда он ссылается на порядок ваших заявлений, он, по-видимому, говорит о формулировке вашего запроса, что для целей оценки несущественно. Последовательные SQL операторы выполняются последовательно, как представлено. Это гарантируется стандартом SQL.

+0

Хороший вопрос о «функциях». Я отредактировал свой ответ, чтобы обратиться к «условиям» – LoztInSpace

Смежные вопросы