SQL "між" не включено


136

У мене такий запит:

SELECT * FROM Cases WHERE created_at BETWEEN '2013-05-01' AND '2013-05-01'

Але це не дає результатів, хоча є дані на 1-му.

created_atсхоже 2013-05-01 22:25:19, я підозрюю, що це стосується часу? Як це можна було вирішити?

Це добре працює, якщо я роблю більші діапазони дат, але він повинен (включно) працювати і з однією датою.


23
Ну, скільки цифр між 1 і 1? Чи має бути 1,5 між 1 і 1? Просто не використовуйте МЕЖДУ для діапазонів дат / часу. Колись. І будьте уважні, як ви оцінюєте "працює просто чудово" - чи уважно ви ознайомилися з результатами останнього дня в діапазоні? Ви б включали всі рядки, лише якщо вони не мали жодного часу, пов'язаного з ними.
Аарон Бертран

Оновлена ​​URL-адреса для Aaron: sqlblog.org/2011/10/19/…
JayRizzo

Відповіді:


294

Це є включно. Ви порівнюєте дату з датами. Друга дата трактується як півночі, коли день починається .

Один із способів виправити це:

SELECT *
FROM Cases
WHERE cast(created_at as date) BETWEEN '2013-05-01' AND '2013-05-01'

Ще один спосіб виправити це - явні бінарні порівняння

SELECT *
FROM Cases
WHERE created_at >= '2013-05-01' AND created_at < '2013-05-02'

У Аарона Бертран є довгий запис у блозі про дати ( тут ), де він обговорює цю та інші проблеми з датою.


15
Для повноти цієї гарної відповіді я б запропонував використовувати dateaddнаступний день.
Тім Ленер

7
@TimLehner Для гарної відповіді я використовував би ISO-8601 формат '20130501'. Для людей, які не входять до США, з датою формату dmy, ви отримуєте це:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi - Я вважаю, що явний біт використовує інтервал, закритий на одному кінці ( >=) і відкритий на іншому ( <), у поєднанні з перевіркою на один день після зазначеної кінцевої дати.
HABO

3
@scottb Особисто мені здається, що це дратує, що потрібно перетворювати на дату кожного разу, коли мені захотілося відобразити, експортувати, імпортувати або написати клас із датою. Я думаю, що SQL Server має багато вбудованих функціональних можливостей для маніпулювання та порівняння дат для більшості цілей.
Тім Ленер

10
Ніколи не слід кидати стовпчик у whereпункті, оскільки він втратить будь-яку індексацію. Це дійсно поганий зразок.
Бузінас

49

Передбачалося, що посилання на другу дату в BETWEENсинтаксисі магічно вважається "кінцем дня", але це неправда .

тобто цього очікувалося:

ВИБІР * ІЗ справ 
ДЕ створено_ТЕ ПІД початок '2013-05-01' І кінець '2013-05-01'

але насправді це:

ВИБІР * ІЗ справ 
ДЕ створив_БЕЗ '2013-05-01 00: 00: 00 + 00000 ' І '2013-05-01 00: 00: 00 + 00000 '

Що стає еквівалентом:

ВИБІР * ВІД випадків, де створено_at = '2013-05-01 00: 00: 00 + 00000 '

Проблема полягає в сприйнятті / очікування про BETWEENякому дійсно включають як нижнє значення і верхні значення в діапазоні, але не чинить чарівно зробити дату «початку» або «кінець».

BETWEEN слід уникати під час фільтрації за діапазонами дат.

Завжди використовуйте >= AND <натомість

ВИБІР * ІЗ справ 
WHERE (created_at > = '20130501' AND created_at < '20130502')

дужки тут необов’язкові, але вони можуть мати важливе значення у складніших запитах.


2
Не впевнені в розумності розміщення цього запитання після того, як на це питання вже відповіли, але я хотів підкреслити трохи інший момент
Used_By_Already

5
До редакторів; будь ласка, не намагайтеся перетворити псевдо SQL у кодові блоки, він просто не працює, оскільки коментар покладається на BOLD.
Б / у_Бі_Авже

2
Редакціям (знову ж таки) не додайте помилкових лапок через псевдокод; вони НЕ сприяють розумінню.
Б / у_Бі_Авже

18

Вам потрібно зробити один із цих двох варіантів:

  1. Включіть часовий компонент у свій betweenстан: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(не рекомендується ... див. Останній абзац)
  2. Використовуйте замість нерівностей between. Зауважте, що тоді вам доведеться додати один день до другого значення:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Мої особисті переваги - другий варіант. Крім того , Аарон Bertrand має дуже ясне пояснення про те, чому вона повинна бути використана.


6
+1 для 2. Але -1 для 1. Цей злом в кінці дня є абсолютно ненадійним і поганою ідеєю.
Аарон Бертран

1
@AaronBertrand Я також віддаю перевагу варіант 2 (я його часто використовую). Але чому ви вважаєте, що варіант 1 "абсолютно ненадійний"?
Barranka

5
будь ласка, прочитайте це повністю . Ніколи не слід використовувати BETWEENдля запитів діапазону дат, що включають час; дуже багато може піти не так.
Аарон Бертран


7

Я вважаю, що найкращим рішенням для порівняння поля дати з полем дати є таке:

DECLARE @StartDate DATE = '5/1/2013', 
        @EndDate   DATE = '5/1/2013' 

SELECT * 
FROM   cases 
WHERE  Datediff(day, created_at, @StartDate) <= 0 
       AND Datediff(day, created_at, @EndDate) >= 0 

Це еквівалентно включній між заявою, оскільки вона включає як дату початку і кінці, так і ті, що перебувають між ними.


3
cast(created_at as date)

Це буде працювати лише у 2008 році та новіших версіях SQL Server

Якщо ви використовуєте старішу версію, тоді використовуйте

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)результат схожий dd/MM/yyyyі рядки ОП є yyyy-MM-dd, я думаю, що ця відповідь не буде працювати;).
shA.t

2

Ви можете скористатися date()функцією, яка вилучає дату з дати та дає результат як включну дату:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' AND '2013-05-01'

0

Динамічна дата між запитом sql

var startDate = '2019-08-22';
var Enddate = '2019-10-22'
     let sql = "SELECT * FROM Cases WHERE created_at BETWEEN '?' AND '?'";
     const users = await mysql.query( sql, [startDate, Enddate]);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.