Миграция на SQL Server 2017: один и тот же запрос иногда не возвращает строк - PullRequest
0 голосов
/ 10 октября 2018

Я только что обновился до SQL Server 2017 с SQL Server 2014 (пока только для среды разработки) и заметил очень странное поведение: один и тот же запрос имеет два разных влияния на оба сервера.

Запрос был переписан и сведен к минимуму для примера:

SELECT * FROM eth_Mandate M WHERE customerId = 1 AND M.isActive = 1

Поле isActive является вычисляемым столбцом (возвращает 1, если мандат равенактивный, 0 в противном случае) определяется как:

[isActive]  AS (CASE WHEN GETDATE() >= ISNULL([man_start],GETDATE()) AND GETDATE() <= isnull(dateadd(day,(1),[man_stop]),GETDATE()) THEN 1 ELSE 0 END)

Для SQL Server 2014 это всегда работает.В 2017 году один раз в 4-5 раз он не возвращает строк, так как иногда isActive вычисляется как 0.

Почему?Я глубоко искал и хотел поделиться им, чтобы вы могли найти мой собственный ответ ниже объяснения, которое я понял, основываясь на моих тестах.

1 Ответ

0 голосов
/ 10 октября 2018

Проблема в том, что в ISNULL у нас есть datetime2 (поле man_stop) и getdate в качестве запасного (возвращает datetime) и, соответственно, ISNULL приводит второй параметр на основе первого параметра .... Следовательно, мы сравниваемgetdate () как datetime с getdate () как datetime2.Обратите внимание, что man_stop (конец мандата клиента) в большинстве случаев не равен нулю, поскольку мандат выполняется.

Затем вы должны взглянуть на эту статью и, в частности, напредложение:

Что касается временной части, SQL Server начинается с 0 и увеличивает числовое значение для каждых 0,003 секунд после полуночи.Что означает ?если время 12: 15: 00: 002 фактически сохраняется как 12: 15: 00: 003, но сравнивается с 12: 15: 00.002, то есть не равно !!

РЕШЕНИЕ 1

Итак, чтобы исправить проблему для SQL Server 2017 (хотя нет проблем с SQL Server 2014), я просто преобразовал свои поля datetime2 в datetime, чтобы иметь правильное сравнение с getdate () в качестве datetime:

[isActive]  AS (case when getdate()>=isnull(CONVERT([datetime],[man_start]),getdate()) AND getdate()<=isnull(dateadd(day,(1),CONVERT([datetime],[man_stop])),getdate()) then (1) else (0) end)

РЕШЕНИЕ 2

Или я также мог бы преобразовать все время даты в datetime2.Вместо GETDATE () используйте SYSDATETIME (), возвращая datetime2 (7).Поскольку мои два поля просто datetime2 (точность не определена), сравнение было хуже ... но при преобразовании datetime2 в datetime2 (7) оно работало как шарм.Таким образом, он становится:

(case when SYSDATETIME () >= isnull(convert(datetime2(7), [man_start]),SYSDATETIME()) AND SYSDATETIME() <= isnull(dateadd(day,(1),convert(datetime2(7), [man_stop])),SYSDATETIME()) then (1) else (0) end)

РЕШЕНИЕ 3

Измените уровень совместимости базы данных на 120 или ниже, чтобы сохранить устаревшее поведение при сравнении даты и времении datetime 2 типа.Это критическое изменение было введено в SQL Server 2016.

РЕШЕНИЕ 4 (решение применено)

Используйте данные того же типа и избегайте преобразования типов.Измените тип полей man_start и man_stop как datetime2 (7) (вместо datetime2, который является datetime2 (3)), чтобы соответствовать возвращаемому типу функции SYSDATETIME () (datetime2 (7)).

ALTER TABLE myTable ALTER COLUMN [man_start] datetime2(7) NULL
ALTER TABLE myTable ALTER COLUMN [man_stop] datetime2(7) NULL  

Таким образом, мой вычисляемый столбец становится следующим: [isActive] AS (СЛУЧАЙ, КОГДА SYSDATETIME ()> = ISNULL ([man_start], SYSDATETIME ()) И SYSDATETIME () <= isnull (dateadd (day, (1), [man_stop])), SYSDATETIME ()) THEN 1 ELSE 0 END) </p>

Преимущества: сохраняется уровень совместимости, избегайте преобразования типов (улучшенная производительность)

...