T-SQL между путаницей дат - PullRequest
       70

T-SQL между путаницей дат

8 голосов
/ 25 марта 2011

Я работаю с T-SQL в SQL Server 2000, и у меня есть таблица TRANSACTIONS, в которой столбец даты TRANDATE определен как DateTime, среди многих других столбцов, которые не имеют отношения к этому вопросу.

Таблица заполнена транзакциями, охватывающими многие годы.Я столкнулся с кодом, тест, который меня смутил.Существует простой SELECT, например:

SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010' and '12/31/2010' ORDER BY TRANDATE

, и он не возвращает две строки данных, которые, как я знаю, находятся в этой таблице.

С приведенным выше оператором последняя строка, которую он возвращает, по порядку имеет TRANDATE из: 2010-12-31 00: 00: 00.000

Когда я изменяю оператор, как показано нижеЯ получаю две дополнительные строки за декабрь 2010 года, которые находятся в этой таблице:

SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010 00:00:00' and '12/31/2010 23:59:59' ORDER BY TRANDATE

Я попытался выяснить, почему оператор BETWEEN не включает ВСЕ строки за 24 периода в 12/31 /2010 при использовании первого SELECT, выше.И почему необходимо добавить явные часы в оператор SELECT, как во втором, измененном операторе, чтобы заставить его вытягивать правильное количество строк?

Это из-за того, что TRANDATE определяется как "DATETIME"?

Исходя из этого, я думаю, что мне придется пройти через весь этот старый код, потому что эти BETWEEN операторы замусорены во всей этой старой системе, и кажется, что она не вытягивает все данные должным образом,Я просто хотел разъяснений от некоторых людей в первую очередь.Спасибо!

Ответы [ 5 ]

14 голосов
/ 25 марта 2011

Дата - это точка во времени, а не промежуток времени.

'12/31/2010' тоже точка.А именно, это полночь 31 декабря.
Все, что произошло после этой точки, игнорируется.
Это именно то поведение, которое вы хотите (даже если вы этого еще не осознали).

Не думайте, что когда вы решите опустить временную часть, она волшебным образом считается равной "any".Это будет "all zeroes", то есть полночь.

Если вы хотите включить в свой запрос весь день без указания 23:59:59 (что, кстати, исключаетпоследняя секунда дня , между моментом 23:59:59 текущего дня и моментом 00:00:00 следующего дня), вы можете сделать это либо с помощьюстрогие неравенства (>, <), ограниченные первыми точками времени, которые вы не хотите:

WHERE TRANDATE >='12/01/2010 00:00:00' and TRANDATE < '01/01/2011'

или путем сравнения значений даты, приведенных к DATE:

WHERE CAST(TRANDATE AS DATE) between '12/01/2010' and '12/31/2010'

(можно использовать этот тип броска в предложении WHERE, это возможно ).

4 голосов
/ 25 марта 2011

Как вы обнаружили, если вы не укажете время при вводе даты, по умолчанию будет полночь утра даты. Поэтому 31.12.2010 останавливается в полночь, когда начинается этот день.

Чтобы получить все даты на 31.12.2010, вы можете либо указать время , как вы сделали, или добавить один день к дате окончания . Без времени 01.01.2011 заканчивается в полночь 31.12.2010. Итак, вы могли бы сделать BETWEEN 12/1/2010 AND 1/1/2011. Вы можете использовать DATEADD, чтобы добавить день в ваш SQL, если это облегчит задачу.

Существует некоторый риск при втором подходе добавления дня. Вы получите любые записи за 01.01.2011, которые содержат время 00: 00: 00.

Вот один из способов выполнения DATEADD:

DECLARE @FromDate datetime, @ToDate datetime
// These might be stored procedure input parameters
SET @FromDate = '12/1/2010'
SET @ToDate = '12/31/2010'

SET @ToDate = DATEADD(d, 1, @ToDate)

Затем вы используете @ToDate в своем предложении WHERE во фразе BETWEEN обычным способом.

3 голосов
/ 25 марта 2011

«12/01/2010» означает «12/01/2010 00:00:00», а «12/31/2010» означает «12/31/2010 00:00:00». Вот почему значения даты и времени, которые выпадают позже в день 31.12.2010, исключаются из результатов вашего запроса.

2 голосов
/ 25 марта 2011

Каким был бы ваш ожидаемый результат, если бы я сделал это

Insert "12/31/2010" into your datetime column?

Точно: 12-31-2010 00: 00: 00

Так почему вы ожидаете, что он будет другим?в качестве аргумента для запроса?

1 голос
/ 25 марта 2011

Вы уже ответили на свой вопрос. То, что вы наблюдали, - это способ работы SQL Server.

Если вам нужно подтверждение, этот документ MSDN имеет следующее слово, чтобы сказать об этом

Когда часть времени не указана, она по умолчанию до 12:00 Обратите внимание, что строка который содержит часть времени, которая является после 12:00 1998-0105 будет не будет возвращен этим запросом, потому что оно выходит за пределы диапазона.

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

Что касается вашего комментария, datetime по сути является значением с плавающей запятой.

Следующий скрипт показывает, с какими числами работает SQL Server.
40541.9749 (31.12.2010 23:23:59) не может быть включено, если ваша верхняя граница равна 40541 (31.12.2010)

DECLARE @ADateTime1 DATETIME
DECLARE @ADateTime2 DATETIME
DECLARE @ADateTime1AsFloat FLOAT
DECLARE @ADateTime2AsFloat FLOAT

SET @ADateTime1 = '12/31/2010'
SET @ADateTime2 = '12/31/2010 23:23:59'

SET @ADateTime1AsFloat = CAST(@ADateTime1 AS FLOAT)
SET @ADateTime2AsFloat = CAST(@ADateTime2 AS FLOAT)

SELECT @ADateTime1AsFloat, @ADateTime2AsFloat
...