Відповіді:
Оскільки ви використовуєте 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
:
datetime
підтримки тільки 3 знаків після коми .. і , отже , ви бачите скругление проблеми , так як за замовчуванням datetime
раундів найближчим .003 seconds
з кроком .000
, .003
або .007
секундами.datetime2
набагато точніше datetime
і datetime2
дає вам контроль DATE
і TIME
на відміну від цього datetime
.Довідка:
DateTime2
vs DateTime
.: a. Для - переважних - більшості - реального використання - випадки, переваги 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:
Діапазон часу - з 00:00 до 23:59: 59.997
Зауважте, що часовий діапазон ніколи не може бути .999
. Далі в документації він визначає правила округлення, які використовує SQL Server для найменш значущої цифри.
Зауважте, що найменш значуща цифра може мати лише одне з трьох потенційних значень: "0", "3" або "7".
Для цього існує декілька рішень / шляхів вирішення.
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
З п'яти запропонованих вище варіантів я б вважав варіанти 1 і 3 єдиними життєздатними варіантами. Вони чітко передають ваш намір і не збираються порушуватися, якщо ви оновлюєте типи даних. Якщо ви використовуєте SQL Server 2008 або новішу версію, я думаю, що варіант 3 повинен бути вашим кращим підходом. Це особливо вірно, якщо ви можете перейти від використання DATETIMEтипу даних до DATEтипу даних для вашого posted_date
стовпця.
Щодо варіанта 3, тут можна знайти дуже гарне пояснення щодо деяких питань: На сьогоднішній день трансляція є важкою, але чи це гарна ідея?
Мені не подобаються варіанти 2 і 5, тому що .997
дробові секунди будуть просто ще одним магічним числом, яке люди хочуть «виправити». З інших причин, чому ви BETWEEN
не отримали широкого поширення, ви хочете оформити цю публікацію .
Мені не подобається варіант 4, оскільки перетворення типів даних у рядок для цілей порівняння мені стає брудним. Більш якісна причина уникнути цього в SQL Server полягає в тому, що він впливає на зручність роботи, так як ви не можете виконати пошук індексу, і це часто призводить до більш низької продуктивності.
Для отримання більш докладної інформації про правильному шляху і неправильному шляху до запитів дати ручки діапазону витягує цей пост за Aaron Bertrand .
При розставанні ви зможете зберегти свій оригінальний запит, і він буде вести себе за бажанням, якщо ви зміните свій posted_date
стовпець з " DATETIMEна" DATETIME2(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) цифр точності? Найкраща практика - використовувати значення, де точність не має значення, тобто <початок наступної секунди, хвилини, години або дня проти <= кінець попередньої секунди, хвилини, години або дня.
Неявне перетворення
Я вважаю, що тип даних data_date - це Datetime. Однак не має значення, чи є тип на іншій стороні Datetime, Datetime2 або просто Time, оскільки рядок (Varchar) буде неявно перетворений у Datetime.
Якщо posts_date оголошений як Datetime2 (або Time), posted_date <= '2015-07-27 23:59:59.99999'
пункт де виходить з ладу, оскільки хоч 23:59:59.99999
це дійсне значення Datetime2, це не є дійсним значенням Datetime:
Conversion failed when converting date and/or time from character string.
Діапазон часу для дати
Діапазон часу в режимі Datetime - від 00:00 до 23:59: 59.997. Тому 23: 59: 59.999 знаходиться поза межами діапазону і має бути закруглено вгору або вниз до найближчого значення.
Точність
Окрім того, значення Datetime округляються з кроком до .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 або Time
Datetime2 і часу інтервали часу є 00:00:00.0000000
через23:59:59.9999999
з точністю 100ns (остання цифра при використанні з точністю 7 цифр).
Однак діапазон Datetime (3) не схожий на діапазон Datetime:
0:0:00.000
до23:59:59.997
0:0:00.000000000
до23:59:59.999
Рішення
Зрештою, безпечніше шукати дати нижче наступного дня, ніж дати нижче або рівні тому, що ви вважаєте останнім фрагментом часу дня. Це головним чином тому, що ви знаєте, що наступний день завжди починається з 0: 00: 00.000, але різні типи даних можуть не мати однакового часу в кінці дня:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000
дасть точні результати і є найкращим варіантом<= 2015-07-27 23:59:59.xxx
може повернути несподівані значення, коли воно не округлене до того, що, на вашу думку, повинно бути.Ми можемо подумати, що зміна [posted_date] на Datetime2 та її більш висока точність можуть вирішити цю проблему, але це не допоможе, оскільки рядок все ще перетворений на Datetime. Однак, якщо додано акторський склад cast(2015-07-27 23:59:59.999' as datetime2)
, це працює чудово
У ролях і конвертувати
Cast може конвертувати значення з трьома цифрами у 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Це своєрідне виправлення проблеми, пов’язане з тимчасовим часом, з кроком 0, 3 і 7, хоча все одно завжди краще шукати дати до 1-ї нано секунди наступного дня (завжди 0: 00: 00.000).
Джерело MSDN: дата (Transact-SQL)
Це округлення
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 всі литі / круглі до .997
Слід використовувати
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
або
where cast(posted_date as date) = '2015-07-27'
Точність див. За цим посиланням
Завжди повідомляється як .000, .003, .007
select * from A where date(posted_date) = '2015-07-27'
'DATE' is not a recognized built-in function name.
gives you control of DATE and TIME as opposed to datetime.
що це означає?