La clause MySQL «between» n'est pas incluse?

142

Si j'exécute une requête avec une betweenclause, cela semble exclure la valeur de fin.
Par exemple:

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

Ceci obtient tous les résultats avec dobdu «2011-01-01» au «2011-01-30»; sauter les enregistrements où dobest «2011-01-31». Quelqu'un peut-il expliquer pourquoi cette requête se comporte de cette façon et comment je pourrais la modifier pour inclure des enregistrements où se dobtrouve «2011-01-31»? (sans ajouter 1 à la date de fin car il a été sélectionné par les utilisateurs.)

ASD
la source
Nan. Mon installation MySQL (version?) BETWEENInclut les deux valeurs. J'ai MySQL Server 5.7sur Windows 10.
Vert

Réponses:

181

Le champ a dobprobablement une composante temporelle.

Pour le tronquer:

select * from person 
where CAST(dob AS DATE) between '2011-01-01' and '2011-01-31'
tiago2014
la source
59
Au lieu de CAST(dob AS DATE)vous, vous pouvez utiliser le plus succinct DATE(dob).
jkndrkn
11
Tant que cela fonctionne, vous obtiendrez de meilleures performances en utilisant >=et <au lieu de between.
David Harkness
112
Vous obtiendrez de meilleures performances en utilisant dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. En effet, DATE(dob)il faut calculer une valeur pour chaque ligne et ne peut utiliser aucun index sur ce champ.
joshuahedlund
2
@joshuahedlund Veuillez ajouter une réponse avec cette solution. CAST n'est pas aussi efficace.
doc_id
3
@joshuahedlund Cela fonctionne jusqu'à ce que vous ayez des données avec les temps t > 23:59:59 and t < 24:00:00. Pourquoi traiter du tout mal spécifié BETWEEN? Suivez plutôt les conseils et l' utilisation de David: WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. Meilleures performances, et cela fonctionne à chaque fois.
Désillusionné le
300

À partir du manuel MySQL :

Ceci est équivalent à l'expression (min <= expr AND expr <= max)

Frank Heikens
la source
3
Le manuel lié à cette réponse montre qu'une distribution est préférable lors de la comparaison des objets DATE et DATETIME. Donc, je suppose que @tiagoinu a la réponse la plus complète au sens strict, mais les deux sont parfaits.
Kingsolmn
@jemminger peut - être parce que la réponse est de Archrival gars -postgres: P
Nawfal
27
En bref, entre est inclusif ... c'est pourquoi cette réponse bascule.
Rafael
6
Ancien commentaire, mais je voulais lier cela à la requête spécifique. "BETWEEN" est inclusif, mais les dates sans heure spécifiée sont à 00:00:00. La comparaison sur une plage de dates perdra donc le dernier jour. Appelez DATE (dob) ou spécifiez la fin de la journée.
wintermute92
ils disent que la pratique est de l'or, d'après mon cas d'utilisation, elle n'est pas du tout inclusive, je me demande pourquoi cela arrive avec moi. J'ai essayé et parfois ça marche parfois pas. en l'utilisant sur le champ de données TIME.
Jeffery ThaGintoki
99

Le problème est que 2011-01-31 est vraiment 2011-01-31 00:00:00. C'est le début de la journée. Tout pendant la journée n'est pas inclus.

Daniel Hilgarth
la source
19
Cela explique vraiment ce qui se passe et répond à la question.
Ivan P
3
Après toutes ces années, cette réponse est toujours la meilleure. Merci beaucoup.
Strabek
31
select * from person where dob between '2011-01-01 00:00:00' and '2011-01-31 23:59:59'
Gaurav
la source
1
Je pense qu'il vaut la peine de noter que cela n'inclura pas les dates à 2011-01-31 23:59:59mais inclura celles jusqu'à 2011-01-31 23:59:58 la dernière seconde de la journée n'est pas inclus. Cela pourrait être mineur mais quelqu'un en bénéficiera.
doc_id
1
rahmanisback de la documentation MySQL Je peux confirmer que la dernière seconde sera incluse puisque BETWEEN est inclusif dans les deux sens. voir dev.mysql.com/doc/refman/5.5/en/…
Felype
1
Oui, @Felype vous avez raison. J'ai vérifié cela moi-même dans la base de données mysql. Il inclut également le 23:59:59dans le résultat. Donc, c'est dans les deux sens inclusif.
Lucky
2
Si la dobcolonne est un horodatage avec une précision inférieure à la seconde, alors ne BETWEENmanquera pas les événements dans la dernière seconde de la journée à moins que «2011-02-01 00:00:00» ne soit utilisé à la place?
azote
1
-1. N'inclura pas 2011-01-31 23:59:59.003. L'utilisation de @nitrogen 2011-02-01 000:00:00inclura à tort l' heure zéro le 1er février .... C'est pourquoi >=et <devrait être utilisé à la place.
Désillusionné le
6

Le champ que vous référencez dans votre requête est-il de type Date ou de type DateHeure ?

Une cause courante du comportement que vous décrivez est lorsque vous utilisez un type DateTime alors que vous devriez vraiment utiliser un type Date. Autrement dit, à moins que vous n'ayez vraiment besoin de savoir à quelle heure une personne est née, utilisez simplement le type Date.

La raison pour laquelle le dernier jour n'est pas inclus dans vos résultats est la façon dont la requête prend en compte la partie horaire des dates que vous n'avez pas spécifiées dans votre requête.

Autrement dit: votre requête est interprétée comme jusqu'à minuit entre le 30/01/2011 et le 31/01/2011, mais les données peuvent avoir une valeur plus tard dans la journée, le 31/01/2011.

Suggestion: remplacez le champ par le type Date s'il s'agit d'un type DateHeure.

JohnFx
la source
4

Salut cette requête fonctionne pour moi,

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

De manière surprenante, ces conversions sont des solutions à de nombreux problèmes dans MySQL.

betty.88
la source
10
Étonnamment, c'est exactement ce que la réponse acceptée (et plusieurs autres) disait ... 2 ans avant vous.
Chris Baker
0

Définissez la date supérieure à date + 1 jour, donc dans votre cas, définissez-la sur 2011-02-01.

Rafal
la source
1
Cela inclura à tort l' heure zéro le 1er février .... C'est pourquoi BETWEENil faut l'ignorer; mais >=et <devrait être utilisé à la place.
Désillusionné le
0

Vous pouvez exécuter la requête en tant que:

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

comme d'autres l'ont souligné, si vos dates sont codées en dur.

En revanche, si la date est dans une autre table, vous pouvez ajouter un jour et soustraire une seconde (si les dates sont enregistrées sans la seconde / heure), comme:

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

Évitez de faire des lancers sur les dobchamps (comme dans la réponse acceptée), car cela peut causer d'énormes problèmes de performances (comme ne pas pouvoir utiliser un index sur le dobterrain, en supposant qu'il y en ait un). Le plan d'exécution peut changer de using index conditionà using wheresi vous faites quelque chose comme DATE(dob)ou CAST(dob AS DATE), alors soyez prudent!

Lucas Basquerotto
la source
0

Dans MySql, les valeurs sont inclusives, donc lorsque vous essayez de passer entre '2011-01-01' et '2011-01-31'

il comprendra de 2011-01-01 00:00:00jusqu'à 2011-01-31 00:00:00 donc rien fait dans 2011-01-31 depuis son temps devrait passer de2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

Pour la limite supérieure, vous pouvez changer pour 2011-02-01alors il obtiendra toutes les données jusqu'à2011-01-31 23:59:59

Ambleu
la source