SQL "entre" no incluido

136

Tengo una consulta como esta:

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

Pero esto no da resultados a pesar de que hay datos en el 1er.

created_atparece 2013-05-01 22:25:19, sospecho que tiene que ver con el tiempo? ¿Cómo podría resolverse esto?

Funciona bien si hago rangos de fechas más grandes, pero también debería (inclusive) funcionar con una sola fecha.

JBurace
fuente
23
Bueno, ¿cuántos números hay entre 1 y 1? ¿Debería 1.5 estar entre 1 y 1? Simplemente no use ENTRE para rangos de fecha / hora. Nunca. Y tenga cuidado al evaluar "funciona bien": ¿ha inspeccionado de cerca los resultados del último día en el rango? Solo incluiría todas las filas si no tuvieran tiempo asociado con ellas.
Aaron Bertrand
URL actualizada para Aaron: sqlblog.org/2011/10/19/…
JayRizzo

Respuestas:

296

Que es inclusiva. Estás comparando fechas y horas con fechas. La segunda fecha se interpreta como la medianoche cuando comienza el día .

Una forma de solucionar esto es:

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

Otra forma de solucionarlo es con comparaciones binarias explícitas.

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

Aaron Bertrand tiene una larga entrada en el blog sobre fechas ( aquí ), donde analiza este y otros problemas de fechas.

Gordon Linoff
fuente
15
Para completar esta buena respuesta, sugeriría usar dateaddpara obtener el día siguiente.
Tim Lehner
77
@TimLehner En aras de una buena respuesta, hubiera utilizado el formato ISO-8601 de '20130501'. Para las personas no estadounidenses con dmy de formato de fecha, obtienes esto:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi: creo que el bit explícito está usando un intervalo cerrado en un extremo ( >=) y abierto en el otro ( <) combinado con la comprobación un día más allá de la fecha de finalización especificada.
HABO
3
@scottb Personalmente, me resultaría molesto tener que convertir a fecha y hora cada vez que quisiera mostrar, exportar, importar o escribir una clase con una fecha y hora. Creo que SQL Server tiene muchas funciones integradas para manipular y comparar fechas y horas para la mayoría de los propósitos.
Tim Lehner
10
Nunca debe emitir una columna en la wherecláusula, ya que perderá cualquier indexación que tenga. Es un patrón realmente malo.
Buzinas
49

Se supone que la segunda referencia de fecha en la BETWEENsintaxis se considera mágicamente como el "final del día", pero esto no es cierto .

es decir, esto se esperaba:

SELECCIONAR * DE Casos 
WHERE created_at ENTRE el comienzo de '2013-05-01' Y el final de '2013-05-01'

pero lo que realmente pasa es esto:

SELECCIONAR * DE Casos 
WHERE created_at ENTRE '2013-05-01 00: 00: 00 + 00000 ' Y '2013-05-01 00: 00: 00 + 00000 '

Que se convierte en el equivalente de:

SELECCIONAR * DESDE casos DONDE created_at = '2013-05-01 00: 00: 00 + 00000 '

El problema es uno de percepciones / expectativas sobre BETWEENel que no incluyen tanto el valor más bajo y los valores superiores de la gama, pero no mágicamente hacer una fecha en que el "inicio de la" o "final de".

BETWEEN debe evitarse al filtrar por rangos de fechas.

Utilice siempre el >= AND <lugar

SELECCIONAR * DE Casos 
WHERE (created_at > = '20130501' AND created_at < '20130502')

los paréntesis son opcionales aquí pero pueden ser importantes en consultas más complejas.

Used_By_Already
fuente
2
No está seguro acerca de la conveniencia de la publicación de este pozo después de la pregunta ya ha sido contestada pero quería hacer hincapié en un punto ligeramente diferente
Used_By_Already
55
A los editores; por favor no intente convertir el pseudo SQL en bloques de código, simplemente no funciona ya que el comentario se basa en BOLD.
Used_By_Already
2
Para los editores (nuevamente), por favor, no agregue comillas falsas a través del pseudocódigo; NO ayudan a la comprensión.
Used_By_Already
18

Debe hacer una de estas dos opciones:

  1. Incluya el componente de tiempo en su betweencondición: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(no recomendado ... vea el último párrafo)
  2. Use desigualdades en lugar de between. Tenga en cuenta que luego deberá agregar un día al segundo valor:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Mi preferencia personal es la segunda opción. Además, Aaron Bertrand tiene una explicación muy clara de por qué debería usarse.

Barranka
fuente
66
+1 para 2. Pero -1 para 1. Este truco al final del día es completamente poco confiable y una mala idea.
Aaron Bertrand
1
@AaronBertrand También prefiero la opción 2 (la uso con frecuencia). Pero, ¿por qué dice que la opción 1 es "completamente poco confiable"?
Barranka
55
por favor lea esto en su totalidad . Nunca debe usar BETWEENpara consultas de rango de fechas que incluyan tiempo; demasiado puede salir mal.
Aaron Bertrand
7

Simplemente use la marca de tiempo como fecha:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
fuente
44
La pregunta está etiquetada como SQL Server, y 'DATE' is not a recognized built-in function name.necesita convertir stackoverflow.com/a/20973452/4233593
Jeff Puckett el
7

Creo que la mejor solución para comparar un campo de fecha y hora con un campo de fecha es la siguiente:

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 

Esto es equivalente a una declaración entre inclusiva, ya que incluye tanto la fecha de inicio como la de finalización, así como las que se encuentran entre ellas.

Ted
fuente
3
cast(created_at as date)

Eso funcionará solo en 2008 y versiones más recientes de SQL Server

Si está utilizando una versión anterior, use

convert(varchar, created_at, 101)
Ali Adravi
fuente
2
convert(varchar, created_at, 101)El resultado es como dd/MM/yyyyy las cadenas de OP son yyyy-MM-dd, creo que esta respuesta no funcionará;).
shA.t
2

Puede usar la date()función que extraerá la fecha de una fecha y hora y le dará el resultado como fecha inclusiva:

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

Fecha dinámica entre la 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
fuente