¿La cláusula MySQL "entre" no incluye?

142

Si ejecuto una consulta con una betweencláusula, parece excluir el valor final.
Por ejemplo:

select * from person where dob between '2011-01-01' and '2011-01-31'

Esto obtiene todos los resultados dobdesde '2011-01-01' hasta '2011-01-30'; saltando registros donde dobes '2011-01-31'. ¿Alguien puede explicar por qué esta consulta se comporta de esta manera y cómo podría modificarla para incluir registros donde dobestá '2011-01-31'? (sin agregar 1 a la fecha de finalización porque ha sido seleccionado por los usuarios).

ASD
fuente
No Mi instalación de MySQL (¿versión?) BETWEENIncluye ambos valores. Tengo MySQL Server 5.7en Windows 10.
Verde

Respuestas:

181

El campo dobprobablemente tiene un componente de tiempo.

Para truncarlo:

select * from person 
where CAST(dob AS DATE) between '2011-01-01' and '2011-01-31'
tiago2014
fuente
59
En lugar de CAST(dob AS DATE)usted puede usar el más sucinto DATE(dob).
jkndrkn
11
Si bien esto funciona, obtendrá un mejor rendimiento utilizando >=y en <lugar de between.
David Harkness
112
Obtendrá un mejor rendimiento mediante el uso dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. Esto se debe a que DATE(dob)tiene que calcular un valor para cada fila y no puede usar ningún índice en ese campo.
joshuahedlund
2
@joshuahedlund Agregue una respuesta con esta solución. CAST no es tan eficiente.
doc_id
3
@joshuahedlund Eso funciona hasta que tenga datos con tiempos t > 23:59:59 and t < 24:00:00. ¿Por qué tratar con poco especificado BETWEEN? Más bien seguir el consejo y el uso de David: WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. El mejor rendimiento, y funciona todo el tiempo.
Desilusionado
300

Del manual de MySQL :

Esto es equivalente a la expresión (min <= expr AND expr <= max)

Frank Heikens
fuente
3
El manual vinculado en esta respuesta muestra que se prefiere una conversión al comparar objetos DATE y DATETIME. Así que supongo que @tiagoinu tiene la respuesta más completa en el sentido más estricto, pero ambos son acertados.
Kingsolmn
@jemminger puede ser porque la respuesta es de archirrival -postgres guy: P
nawfal
27
En resumen, el medio es inclusivo ... es por eso que esta respuesta es genial.
Rafael
66
Comentario anterior, pero quería vincular esto con la consulta específica. "ENTRE" es inclusivo, pero las fechas sin hora especifican el pad a 00:00:00. Comparar en un rango de fechas, por lo tanto, perderá el último día. Llame al DATE (dob) o especifique el final del día.
wintermute92
Dicen que la práctica es oro, desde mi caso de uso no es inclusivo en absoluto, me pregunto por qué sucede esto conmigo. Lo intenté y a veces funciona, a veces no. usándolo en el campo de datos TIME.
Jeffery ThaGintoki
99

El problema es que 2011-01-31 realmente es 2011-01-31 00:00:00. Ese es el comienzo del día. Todo durante el día no está incluido.

Daniel Hilgarth
fuente
19
Esto realmente explica lo que está sucediendo y responde la pregunta.
Ivan P
3
Después de todos esos años, esta respuesta sigue siendo la mejor. Muchas gracias.
Strabek
31
select * from person where dob between '2011-01-01 00:00:00' and '2011-01-31 23:59:59'
Gaurav
fuente
1
Yo creo que vale la pena destacar que, este no incluirá las fechas en 2011-01-31 23:59:59que se incluyen los que hasta 2011-01-31 23:59:58 el último segundo del día no se incluye Podría ser menor, pero alguien va a beneficiar.
doc_id
1
rahmanisback de la documentación de MySQL Puedo confirmar que se incluirá el último segundo, ya que ENTRE ambos aspectos está incluido. consulte dev.mysql.com/doc/refman/5.5/en/...
Felype
1
Sí, @Felype tienes razón. Lo comprobé yo mismo en la base de datos mysql. Incluye también el 23:59:59en el resultado. Por lo tanto, sus dos sentidos son inclusivos.
Lucky
2
Si la dobcolumna es una marca de tiempo con una precisión inferior al segundo, ¿no se BETWEENperderán eventos en el último segundo del día a menos que se use '2011-02-01 00:00:00'?
nitrógeno
1
-1. No incluirá 2011-01-31 23:59:59.003. @nitrogen usando 2011-02-01 000:00:00habrá incorrectamente incluir el tiempo cero el 1 de febrero .... ¿Cuál es la razón >=y <se debe utilizar en su lugar.
Desilusionado
6

¿El campo al que hace referencia en su consulta es un tipo de fecha o un tipo de fecha y hora ?

Una causa común del comportamiento que describe es cuando usa un tipo DateTime donde realmente debería estar usando un tipo Date. Es decir, a menos que realmente necesite saber a qué hora nació alguien, simplemente use el tipo Fecha.

La razón por la que el último día no se incluye en sus resultados es la forma en que la consulta asume la parte de tiempo de las fechas que no especificó en su consulta.

Es decir: su consulta se interpreta como hasta la medianoche entre el 30/01/2011 y el 31-01-2011, pero los datos pueden tener un valor en algún momento más tarde en el día 31/01/2011.

Sugerencia: cambie el campo al tipo Fecha si es un tipo Fecha y hora.

JohnFx
fuente
4

Hola, esta consulta me funciona,

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'
infinito84
fuente
2
select * from person where DATE(dob) between '2011-01-01' and '2011-01-31'

Sorprendentemente, tales conversiones son soluciones a muchos problemas en MySQL.

betty.88
fuente
10
Sorprendentemente, esto es exactamente lo que dijo la respuesta aceptada (y varias otras) ... 2 años antes que usted.
Chris Baker
0

Establezca la fecha superior en fecha + 1 día, por lo tanto, en su caso, configúrelo en 2011-02-01.

Rafal
fuente
1
Esto incluirá incorrectamente el tiempo cero el 1 de febrero ... Es por eso que BETWEENdebe ignorarse; pero >=y <debería usarse en su lugar.
Desilusionado
0

Puede ejecutar la consulta como:

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'

como otros señalaron, si sus fechas están codificadas.

Por otro lado, si la fecha está en otra tabla, puede agregar un día y restar un segundo (si las fechas se guardan sin el segundo / hora), como:

select * from person JOIN some_table ... where dob between some_table.initial_date and (some_table.final_date + INTERVAL 1 DAY - INTERVAL 1 SECOND)

Evite realizar lanzamientos en los dobcampos (como en la respuesta aceptada), porque eso puede causar grandes problemas de rendimiento (como no poder usar un índice en el dobcampo, suponiendo que haya uno). El plan de ejecución puede cambiar de using index conditiona using wheresi haces algo como DATE(dob)o CAST(dob AS DATE), ¡así que ten cuidado!

Lucas Basquerotto
fuente
0

En MySql, los valores son inclusivos, por lo tanto, cuando intenta obtener entre '2011-01-01' y '2011-01-31'

incluirá desde 2011-01-01 00:00:00hasta 2011-01-31 00:00:00 por lo tanto, nada en realidad en 2011-01-31 ya que su tiempo debería pasar de2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

Para el límite superior puede cambiar a, 2011-02-01entonces obtendrá todos los datos hasta2011-01-31 23:59:59

Ambleu
fuente