Я создал функцию, которая принимает дату начала и окончания, причем дата окончания является необязательной. Затем я написал CASE
в фильтре, чтобы использовать дату начала, если дата окончания не передана.
CASE WHEN @dateEnd IS NULL
THEN @dateStart
ELSE @dateEnd
END
Когда я вызываю функцию для самого последнего месяца данных:
SELECT * FROM theFunction ('2013-06-01', NULL)
... запрос зависает. Если я укажу дату окончания:
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
... результат возвращается нормально. Я вынул код из функции и запустил его в окне запроса. Я не могу дублировать проблему на скрипке. Запрос как:
SELECT * FROM theFunction ('2013-04-01', '2013-06-01')
... тоже отлично работает.
Есть ли в запросе (ниже) что-нибудь, что может привести к зависанию функции при NULL
передаче значения a на дату окончания?
- План выполнения для
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
- Предполагаемый план на
SELECT * FROM theFunction ('2013-06-01', NULL)
sql-server
sql-server-2008-r2
Кермит
источник
источник
CASE
сCOALESCE(@dateEnd,@dateStart)
, это все еще появляется проблема?ISNULL()
?SELECT task_state FROM sys.dm_os_tasks WHERE session_id = x
показывает? Если он проводит много времени не вRUNNING
состоянии, какие типы ожидания входят в этот сеансsys.dm_os_waiting_tasks
?COALESCE
.ISNULL
починил это.Ответы:
Часть вашего первоначального запроса выглядит следующим образом.
Этот раздел плана показан ниже
Ваш пересмотренный запрос
BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
имеет это для того же соединенияРазница, кажется, в том, что она
ISNULL
еще больше упрощается, и в результате вы получаете более точные статистические данные о количестве элементов в следующем соединении. Это встроенная табличная функция, и вы вызываете ее с литеральными значениями, чтобы она могла что-то вроде.И поскольку есть предикат
b.[Date] = a.d
равного соединения, план также показывает предикат равенстваb.[Date] = '2013-06-01'
. В результате оценка количества28,393
строк, скорее всего, будет довольно точной.Для
CASE
/COALESCE
версии, когда@dateStart
и@dateEnd
являются одним и тем же значением, тогда он упрощает ОК до того же выражения равенства и дает тот же план, но когда@dateStart = '2013-06-01'
и@dateEnd IS NULL
он идет только дочто он также применяется в качестве подразумеваемого предиката
ColleagueList
. Расчетное количество строк на этот раз составляет79.8
ряды.Следующее соединение
colleagueTime
является3,249,590
строка таблицы , которая представляет собой (снова) по- видимому , куча без каких - либо полезных индексов.Это расхождение в оценках влияет на используемый выбор соединения.
ISNULL
План выбирает хеширования , который просто просматривает таблицу один раз.COALESCE
План выбирает вложенные циклы и оценок , что он все равно будет просто необходим сканировать таблицу один раз и иметь возможность намотать результат и повторить его 78 раз. т.е. он оценивает, что коррелированные параметры не изменятся.Исходя из того, что план с вложенными циклами все еще продолжался после двух часов, это предположение об одном сканировании
colleagueTime
кажется весьма неточным.Что касается того, почему предполагаемое количество строк между двумя объединениями намного меньше, я не уверен, не увидев статистику по таблицам. Единственный способ, которым мне удалось исказить приблизительное количество строк, в моем тестировании было добавление загрузки
NULL
строк (это уменьшило приблизительное количество строк, даже если фактическое количество возвращенных строк осталось прежним).Расчетное количество строк в
COALESCE
плане с моими данными испытаний было в порядкеИли в SQL
но это не согласуется с вашим комментарием, что столбец не имеет
NULL
значений.источник
NULL
значений для дат ни в одной из этих таблиц.dbo
нет в списке. Просто другие схемы, которые я не использую.Кажется, что есть проблема с типами данных.
ISNULL
исправил проблему (спасибо ypercube ). После некоторых исследований,COALESCE
является эквивалентом кCASE
утверждению , что я использовал:Пол Уайт объясняет, что:
Похоже, что для избежания проблем с типом данных
ISNULL
целесообразно использовать только два выражения.Выдержки из плана XML
Использование плана XML
CASE
, выражение 2NULL
:Использование плана XML
CASE
, выражение 2 - это дата:Использование плана XML
ISNULL
, выражение 2NULL
:Использование плана XML
ISNULL
, выражение 2 - это дата:источник
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
. Тип данных выражения остается прежним. И оба параметра вdate
любом случае имеют тип данных. Можете ли вы просмотреть планы выполнения?NULL
.CASE
тоже не дало эффекта, запрос все еще зависает.ISNULL
выглядит план , как это упрощает лучше. У него есть простой предикат равенства для ColleagueList,[Date]='2013-06-01'
тогда как уCASE
одного - предикат[Date]>='2013-06-01' AND [Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END AND PROBE([Bitmap1067],[Date])
. Предполагаемые строки, выходящие из этого объединения, составляют 28 393 дляISNULL
версии, но намного ниже79.8
дляCASE
версии, что влияет на выбор объединения позднее в плане. Не уверен, почему будет такое несоответствие.