SQL "entre" não inclusivo

136

Eu tenho uma consulta como esta:

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

Mas isso não dá resultados, mesmo que haja dados no 1º.

created_atparece 2013-05-01 22:25:19, eu suspeito que tem a ver com o tempo? Como isso poderia ser resolvido?

Funciona muito bem se eu fizer períodos maiores, mas também deve funcionar com uma única data.

JBurace
fonte
23
Bem, quantos números estão entre 1 e 1? 1.5 deve estar entre 1 e 1? Apenas não use ENTRE para intervalos de data / hora. Sempre. E tenha cuidado ao avaliar "funciona muito bem" - você examinou de perto os resultados do último dia no intervalo? Você incluiria todas as linhas apenas se elas não tivessem tempo associado a elas.
Aaron Bertrand
URL atualizado para Aaron: sqlblog.org/2011/10/19/…
JayRizzo

Respostas:

296

Ele é inclusiva. Você está comparando datas e datas. A segunda data é interpretada como meia-noite quando o dia começa .

Uma maneira de corrigir isso é:

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

Outra maneira de corrigi-lo é com comparações binárias explícitas

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

Aaron Bertrand tem uma entrada longa no blog sobre datas ( aqui ), onde ele discute esse e outros problemas de datas.

Gordon Linoff
fonte
15
Para completar esta boa resposta, eu sugeriria usar dateaddpara obter o dia seguinte.
Tim Lehner
7
@ TimLehner Por uma boa resposta, eu teria usado o formato ISO-8601 de '20130501'. Para as pessoas não americanas com dateformat dmy, você recebe a seguinte:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi - Eu acredito que o bit explícito está usando um intervalo fechado em uma extremidade ( >=) e aberto na outra ( <) combinado com a verificação de um dia além da data final especificada.
HABO
3
@scottb Pessoalmente, eu consideraria irritante ter que converter em data / hora sempre que quisesse exibir, exportar, importar ou escrever uma classe com data e hora. Eu acho que o SQL Server tem muitas funcionalidades internas para manipular e comparar datas e horários para a maioria das finalidades.
Tim Lehner
10
Você nunca deve converter uma coluna na wherecláusula, pois ela perderá qualquer indexação. É um padrão muito ruim.
Buzinas 5/09/2015
49

Supõe-se que a segunda referência de data na BETWEENsintaxe é magicamente considerada o "fim do dia", mas isso não é verdade .

isto é esperado:

SELECT * FROM Casos 
WHERE created_at ENTRE o início de '2013-05-01' E o final de '2013-05-01'

mas o que realmente acontece é isso:

SELECT * FROM Casos 
WHERE created_at ENTRE '2013-05-01 00: 00: 00 + 00000 ' AND '2013-05-01 00: 00: 00 + 00000 '

Que se torna o equivalente a:

SELECT * FROM Casos WHERE created_at = '2013-05-01 00: 00: 00 + 00000 '

O problema é uma das percepções / expectativas sobre BETWEENo que não incluir tanto o valor mais baixo e os valores superiores da gama, mas não magicamente fazer uma data de "início de" ou "o fim da".

BETWEEN deve ser evitado ao filtrar por períodos.

Sempre usar a >= AND <vez

SELECT * FROM Casos 
WHERE (created_at > = '20130501' AND created_at < '20130502')

os parênteses são opcionais aqui, mas podem ser importantes em consultas mais complexas.

Used_By_Already
fonte
2
Incerto sobre a sabedoria de publicar esse bem após a pergunta já foi respondida, mas eu queria salientar um ponto ligeiramente diferente
Used_By_Already
5
Para editores; por favor, não tente transformar o pseudo SQL em blocos de código, ele simplesmente não funciona, pois o comentário depende do BOLD.
Used_By_Already
2
Para os editores (novamente), não adicione aspas falsas através do pseudo-código; eles NÃO ajudam a compreensão.
Used_By_Already
18

Você precisa fazer uma destas duas opções:

  1. Inclua o componente de tempo em sua betweencondição: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(não recomendado ... consulte o último parágrafo)
  2. Use desigualdades em vez de between. Observe que você precisará adicionar um dia ao segundo valor:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Minha preferência pessoal é a segunda opção. Além disso, Aaron Bertrand tem uma explicação muito clara sobre por que deve ser usado.

Barranka
fonte
6
+1 para 2. Mas -1 para 1. Esse hack no final do dia não é totalmente confiável e é uma má idéia.
Aaron Bertrand
1
@AaronBertrand Também prefiro a opção 2 (eu a uso frequentemente). Mas por que você diz que a opção 1 é "completamente não confiável"?
Barranka
5
por favor leia isto na íntegra . Você nunca deve usar BETWEENpara consultas de período que incluem hora; demais pode dar errado.
Aaron Bertrand
7

Basta usar o carimbo de data / hora como data:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
fonte
4
A pergunta está marcada com o SQL Server e 'DATE' is not a recognized built-in function name.precisa converter stackoverflow.com/a/20973452/4233593
Jeff Puckett
7

Acho que a melhor solução para comparar um campo de data e hora com um campo de data é a seguinte:

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 

Isso é equivalente a uma instrução inclusiva entre, pois inclui a data de início e término, bem como as que se situam entre elas.

Ted
fonte
3
cast(created_at as date)

Isso funcionará apenas em 2008 e em versões mais recentes do SQL Server

Se você estiver usando uma versão mais antiga, use

convert(varchar, created_at, 101)
Ali Adravi
fonte
2
convert(varchar, created_at, 101)resultado é como dd/MM/yyyye cordas do OP são yyyy-MM-dd, acho que esta resposta não vai funcionar;).
shA.t
2

Você pode usar a date()função que extrairá a data de uma data e hora e fornecerá o resultado como data inclusiva:

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

Data dinâmica entre a consulta 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
fonte