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 для Аарона: 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»)

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


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')

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


6
+1 за 2. Но -1 за 1. Этот хак в конце дня совершенно ненадежен и плохая идея.
Аарон Бертран

1
@AaronBertrand Я также предпочитаю вариант 2 (я использую его часто). Но почему вы говорите, что вариант 1 «абсолютно ненадежен»?
Барранка

5
Пожалуйста, прочитайте это полностью . Вы никогда не должны использовать BETWEENдля запросов диапазона дат, которые включают время; слишком много может пойти не так.
Аарон Бертран

7

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

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

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]);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.