SQL getdate () - не то же самое в одном выражении - PullRequest
1 голос
/ 28 января 2020

Оператор getDate() всегда возвращает одно и то же значение в любом месте одного оператора.

Однако в одном SQL Server 2017 я вижу иначе.

Чтобы настроить это , создайте таблицу и поместите в нее две строки:

CREATE TABLE Test 
(
    TestDate datetime2(0) NULL,
    OtherValue varchar(5) NULL
) 

INSERT INTO Test (OtherValue) VALUES ('x')
INSERT INTO Test (OtherValue) VALUES ('x')

Затем выполните этот запрос несколько раз:

SELECT  
    CASE 
       WHEN GETDATE() < COALESCE(TestDate, GETDATE()) 
          THEN 'less'
       WHEN GETDATE() > COALESCE(TestDate, GETDATE()) 
          THEN 'greater'
       ELSE 'same' 
    END [Compare]
FROM 
    Test

Обе строки всегда возвращают совпадающие результаты.

Когда я делаю это на SQL Server 2008 R2 (v10.50) и других SQL Server 2017 машинах, результат всегда «одинаков».

Однако на одном из моих SQL Server В 2017 году оно случайным образом изменяется между «одинаковыми», «меньшими» и «большими»:

enter image description here

enter image description here

enter image description here

Почему это происходит? Существует ли параметр сервера, который может вызвать это?

Редактировать :

Использование SYSDATETIME вместо GETDATE работает как положено на «плохом» сервере, всегда возвращая «то же самое».

Edit # 2 :

Если я проверю GETDATE, как указано выше, в столбце, определенном как DATETIME (что и является GETDATE() генерирует), то работает как положено. Так что, похоже, это связано с конвертацией между DATETIME и DATETIME2.

Ответы [ 2 ]

1 голос
/ 29 января 2020

Достаточно интересный вопрос.

Поведение в вашем примере можно объяснить следующим:

  1. Начиная с SQL Server 2016, округление даты и времени было изменено. Вкратце: с 2016 года SQL Сервер значение не округляется до сравнения, и сравнение выполняется с необработанным значением. До 2016 года SQL Сервер значение округляется и затем сравнивается.
  2. По умолчанию сравнение datetime и datetime2 выполняется с преобразованием datetime в datetime2 (7). Вы можете видеть, что в плане выполнения.
  3. переменная даты и времени с 3 в конце - например, .003 - преобразуется в .0033333. 007 конвертируется в .0066667.
  4. И самая интересная часть: наносекунды. При сравнении SQL Сервер использует 8 (или более!) Цифр в дробной части. Я просто показываю два примера для объяснения.
DECLARE @DateTime datetime = '2016-01-01T00:00:00.003';
DECLARE @DateTime2 datetime2(7) = @DateTime;

select    datepart(NANOSECOND,@DateTime)      as  "DateTimeRes",
      datepart(nanosecond,@DateTime2)     as  "DateTime2Res"
go

DECLARE @DateTime datetime = '2016-01-01T00:00:00.007';
DECLARE @DateTime2 datetime2(7) = @DateTime;

select    datepart(NANOSECOND,@DateTime)      as  "DateTimeRes",
      datepart(nanosecond,@DateTime2)     as  "DateTime2Res"

Результаты:

+-------------+--------------+
| DateTimeRes | DateTime2Res |
+-------------+--------------+
|     3333333 |      3333300 |
+-------------+--------------+

+-------------+--------------+
| DateTimeRes | DateTime2Res |
+-------------+--------------+
|     6666666 |      6666670 |
+-------------+--------------+

Я взял все из этой статьи .

Также существует аналогичный вопрос для SO.

Я считаю, что это поведение не зависит от производительности вашего сервера (виртуальная машина или др. c). Удачи!

0 голосов
/ 29 января 2020

Оказывается, что поведение getdate изменено с SQL 2000 на SQL 2005.

См. { ссылка }, объясняющее старое поведение:

На практике GETDATE () оценивается только один раз для каждого выражения, где оно используется - во время выполнения, а не во время компиляции. Однако Microsoft помещает rand () и getdate () в специальную категорию, называемую недетерминированными c постоянными функциями времени выполнения.

и после обсуждения :

В SQL 2000, если бы вы сделали что-то подобное

INSERT INTO tbl (fields, LOADDATE) SELECT поля, GETDATE () FROM tblb

вы получите то же самое дата / время для всех вставленных записей.

Эта же команда В SQL 2005 перезапускает GETDATE () для каждой записи, выбранной из tblb, и дает вам потенциально уникальные значения для каждой записи. Также вызывает ОГРОМНЫЕ проблемы с производительностью, если вы вставляете, скажем, 17 миллионов строк за один раз.

Это вызвало у меня много головной боли, так как мы используем этот код для выполнения пакетной даты / времени во многих таблицах. Это был очень простой способ отменить «пакет» транзакций, потому что все имели одинаковую дату / время. В 2005 году это не так.

...