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, я подозреваю, что это связано со временем? Как это можно решить?

Это работает очень хорошо, если я делаю большие диапазоны дат, но оно должно (включительно) работать с одной датой тоже.

JBurace
источник
23
Ну, сколько чисел от 1 до 1? 1,5 должно быть между 1 и 1? Только не используйте МЕЖДУ для диапазонов даты / времени. Когда-либо. И будьте осторожны, как вы оцениваете «отлично работает» - внимательно ли вы проверяли результаты последнего дня в диапазоне? Вы бы включили все строки, только если у них не было времени, связанного с ними.
Аарон Бертран
Обновленный URL для Аарона: sqlblog.org/2011/10/19/…
JayRizzo

Ответы:

296

Это является включительно. Вы сравниваете даты и даты. Вторая дата интерпретируется как полночь, когда день начинается .

Один из способов исправить это:

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». Для неамериканских людей с dateformat dmy, вы получите это:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi - Я полагаю, что явный бит использует интервал, закрытый в одном конце ( >=) и открытый в другом ( <), в сочетании с проверкой на один день после указанной конечной даты.
HABO
3
@scottb Лично я нахожу раздражающим необходимость конвертировать в datetime каждый раз, когда я хотел отобразить, экспортировать, импортировать или написать класс с datetime. Я думаю, что 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 »

Что становится эквивалентом:

SELECT * FROM CASE ГДЕ созданный_at = '2013-05-01 00: 00: 00 + 00000 '

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

BETWEEN следует избегать при фильтрации по диапазонам дат.

Всегда используйте >= AND <вместо

ВЫБРАТЬ * ИЗ ДЕЛА 
ГДЕ (созданный_ат > = «20130501» И созданный_т < «20130502»)

круглые скобки здесь необязательны, но могут быть важны в более сложных запросах.

Used_By_Already
источник
2
Не уверен насчет мудрости размещения этого материала после того, как на вопрос уже получен ответ, но я хотел бы подчеркнуть немного другой момент
Used_By_Already
5
Редакторам; пожалуйста, не пытайтесь превратить псевдо-SQL в блоки кода, он просто не работает, так как комментарий опирается на BOLD.
Used_By_Already
2
Редакторам (опять же), пожалуйста, не добавляйте ложные кавычки через псевдокод; они НЕ помогают пониманию.
Used_By_Already
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')

Мои личные предпочтения - второй вариант. Кроме того, Аарон Бертран имеет очень четкое объяснение того, почему его следует использовать.

Barranka
источник
6
+1 за 2. Но -1 за 1. Этот хак в конце дня совершенно ненадежен и плохая идея.
Аарон Бертран
1
@AaronBertrand Я также предпочитаю вариант 2 (я использую его часто). Но почему вы говорите, что вариант 1 «абсолютно ненадежен»?
Барранка
5
Пожалуйста, прочитайте это полностью . Вы никогда не должны использовать BETWEENдля запросов диапазона дат, которые включают время; слишком много может пойти не так.
Аарон Бертран
7

Просто используйте отметку времени в качестве даты:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
источник
4
Вопрос помечен SQL Server, и 'DATE' is not a recognized built-in function name.нужно конвертировать stackoverflow.com/a/20973452/4233593
Джефф
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и строки OP 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]);
Muralidharan C
источник