Поскольку вы используете datetimeтип данных, вам необходимо понять, как сервер sql округляет данные даты и времени.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Используя приведенный ниже запрос, вы можете легко увидеть проблему округления, которую делает sql-сервер при использовании DATETIME
типа данных.
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
нажмите, чтобы увеличить
DATETIME2
существует с SQL Server 2008, поэтому начните использовать его вместо DATETIME
. Для вашей ситуации вы можете использовать datetime2
с точностью до 3 десятичных знаков, например datetime2(3)
.
Преимущества использования datetime2
:
- Поддержка до 7 знаков после запятой для компонента времени против
datetime
поддержки только 3 знаков после запятой .. и , следовательно , вы видите скругление проблемы , так как по умолчанию datetime
раундов ближайшим .003 seconds
с шагом .000
, .003
или .007
секундами.
datetime2
гораздо точнее, чем datetime
и datetime2
дает вам контроль DATE
и TIME
в отличие от datetime
.
Ссылка :
gives you control of DATE and TIME as opposed to datetime.
что это обозначает?DateTime2
противDateTime
: а. Для прецедентов, применяемых в подавляющем большинстве случаев в реальном мире , преимуществаDateTime2
намного выше затрат. См .: stackoverflow.com/questions/1334143/… b. Это не главная проблема здесь. Смотрите следующий комментарий.datetime3
добавить 70 (против 7) цифр точности?). Рекомендуется использовать значение, для которого точность не имеет значения, т. Е. < Начало следующей секунды, минуты, часа или дня по сравнению с <= концом предыдущей секунды, минуты, часа или дня.Как уже упоминали некоторые другие в комментариях и других ответах на ваш вопрос, основная проблема заключается
2015-07-27 23:59:59.999
в том, чтобы2015-07-28 00:00:00.000
SQL Server округлил до нее. Согласно документации для DATETIME:Обратите внимание, что временной диапазон никогда не может быть
.999
. Далее в документации указаны правила округления, которые SQL Server использует для наименее значащей цифры.Обратите внимание, что наименее значимая цифра может иметь только одно из трех возможных значений: «0», «3» или «7».
Есть несколько решений / обходных путей для этого, которые вы можете использовать.
Из пяти представленных выше вариантов я бы рассмотрел варианты 1 и 3 единственно приемлемыми. Они четко передают ваши намерения и не сломаются, если вы обновите типы данных. Если вы используете SQL Server 2008 или новее, я думаю, что вариант 3 должен быть вашим предпочтительным подходом. Это особенно верно, если вы можете перейти от использования DATETIMEтипа данных к типу DATEданных для вашего
posted_date
столбца.Что касается варианта 3, то здесь можно найти очень хорошее объяснение некоторых проблем: приведение к настоящему моменту оправданно, но хорошая ли это идея?
Мне не нравятся варианты 2 и 5, потому что
.997
доли секунды будут просто еще одним магическим числом, которое люди захотят «исправить». По ряду других причин, по которымBETWEEN
это не так широко распространено, вы можете зайти в этот пост .Мне не нравится вариант 4, потому что преобразование типов данных в строку для сравнения кажется мне грязным. Более качественная причина избегать этого в SQL Server - это влияет на саргнитивность, иначе вы не можете выполнять поиск по индексу, и это часто приводит к снижению производительности.
Для получения более подробной информации о правильном пути и неправильном пути к запросам даты ручки диапазона извлекает этот пост по Aaron Bertrand .
На прощание вы сможете сохранить свой исходный запрос, и он будет вести себя как нужно, если вы измените свой
posted_date
столбец с a DATETIMEна aDATETIME2(3)
. Это позволило бы сэкономить место на сервере, повысить точность при той же точности, быть более совместимым со стандартами / переносимым и позволить легко регулировать точность / точность, если ваши потребности изменятся в будущем. Тем не менее, это только вариант, если вы используете SQL Server 2008 или новее.Как небольшая мелочь
1/300
, вторая точность с, DATETIMEкажется, удерживается от UNIX для этого ответа StackOverflow . Sybase, который имеет общее наследие, имеет схожую1/300
вторую точность в их типахDATETIME
иTIME
типах данных, но их младшие значащие цифры - это касание, отличающееся в «0», «3» и «6». По моему мнению,1/300
точность в секунду и / или 3,33 мс является неудачным архитектурным решением, поскольку 4-байтовый блок для времени в DATETIMEтипе данных SQL Server мог бы легко поддерживать точность в 1 мс.источник
datetime3
добавить 70 (против 7) цифр точности? Рекомендуется использовать значение, для которого точность не имеет значения, т. Е. <Начало следующей секунды, минуты, часа или дня по сравнению с <= концом предыдущей секунды, минуты, часа или дня.Я предположил, что тип данных posts_date - Datetime. Однако не имеет значения, является ли тип на другой стороне Datetime, Datetime2 или просто Time, потому что строка (Varchar) будет неявно преобразована в Datetime.
С объявленной в качестве Datetime2 (или Time) posts_date
posted_date <= '2015-07-27 23:59:59.99999'
условие where не выполняется, поскольку хотя и23:59:59.99999
является допустимым значением Datetime2, это не является допустимым значением Datetime:Диапазон времени Datetime - от 00:00:00 до 23: 59: 59.997. Поэтому 23: 59: 59.999 находится вне диапазона и должен округляться вверх или вниз до ближайшего значения.
Кроме того, значения даты и времени округляются с приращением .000, .003 или .007 секунд. (т. е. 000, 003, 007, 010, 013, 017, 020, ..., 997)
Это не относится к значению,
2015-07-27 23:59:59.999
которое находится в этом диапазоне:2015-07-27 23:59:59.997
и2015-07-28 0:00:00.000
.Этот диапазон соответствует ближайшему предыдущему и последующему опциям, заканчивающимся либо на .000, .003 или .007.
Потому что ближе к
2015-07-28 0:00:00.000
(+1 по сравнению с -2) , чем2015-07-27 23:59:59.997
строка округляется и становится это значение Datetime:2015-07-28 0:00:00.000
.С верхним пределом, таким как
2015-07-27 23:59:59.998
(или .995, .996, .997, .998), он был бы округлен до,2015-07-27 23:59:59.997
и ваш запрос работал бы как ожидалось. Однако это было бы не решением, а просто счастливой ценностью.Datetime2 и времени интервалы времени являются
00:00:00.0000000
через23:59:59.9999999
с точностью до 100 нс (последняя цифра при использовании с 7 цифр точности а).Однако диапазон Datetime (3) не похож на диапазон Datetime:
0:0:00.000
и время23:59:59.997
0:0:00.000000000
до23:59:59.999
В конце концов, безопаснее искать даты ниже следующего дня, чем даты ниже или равные тому, что вы считаете последним фрагментом времени дня. Это в основном потому, что вы знаете, что следующий день всегда начинается в 0: 00: 00.000, но разные типы данных могут не иметь одинакового времени в конце дня:
< 2015-07-28 0:00:00.000
даст вам точные результаты и является лучшим вариантом<= 2015-07-27 23:59:59.xxx
может возвращать неожиданные значения, когда оно не округлено до того, которое, по вашему мнению, должно быть.Мы могли бы подумать, что изменение [posts_date] на Datetime2 и его более высокую точность может решить эту проблему, но это не поможет, потому что строка все еще преобразуется в Datetime. Однако, если добавлен приведение
cast(2015-07-27 23:59:59.999' as datetime2)
, это работает нормальноПриведение может преобразовывать значение длиной до 3 цифр в Datetime или до 9 цифр в Datetime2 или Time и округлять его с правильной точностью.
Следует отметить, что Cast of Datetime2 и Time2 могут давать разные результаты:
select cast('20150101 23:59:59.999999999' as datetime2(7))
округляется до 2015-05-03 00: 00: 00.0000000 (для значения больше 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Это своего рода устраняет проблему, возникающую у datetime с приращениями 0, 3 и 7, хотя все же всегда лучше искать даты до первой наносекунды следующего дня (всегда 0: 00: 00.000).
Источник MSDN: дата и время (Transact-SQL)
источник
Это округление
.998, .997, .996, .995 все приведение / округление до .997
Следует использовать
или
Смотрите точность в этой ссылке.
Всегда указывается как .000, .003, .007
источник
источник
'DATE' is not a recognized built-in function name.