SQL «entre» non inclusif

136

J'ai une requête comme celle-ci:

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

Mais cela ne donne aucun résultat même s'il existe des données sur le 1er.

created_atressemble 2013-05-01 22:25:19, je soupçonne que cela a à voir avec le temps? Comment cela pourrait-il être résolu?

Cela fonctionne très bien si je fais des plages de dates plus grandes, mais cela devrait également fonctionner avec une seule date.

JBurace
la source
23
Eh bien, combien de nombres sont compris entre 1 et 1? Est-ce que 1,5 doit être compris entre 1 et 1? N'utilisez pas BETWEEN pour les plages de date / heure. Déjà. Et faites attention à la façon dont vous évaluez "fonctionne très bien" - avez-vous inspecté de près les résultats du dernier jour de la gamme? Vous n'incluriez toutes les lignes que si aucune heure ne leur était associée.
Aaron Bertrand
URL mise à jour pour Aaron: sqlblog.org/2011/10/19/…
JayRizzo

Réponses:

296

Il est inclus. Vous comparez les dates et les dates. La deuxième date est interprétée comme minuit lorsque la journée commence .

Une façon de résoudre ce problème est:

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

Une autre façon de résoudre ce problème consiste à utiliser des comparaisons binaires explicites

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

Aaron Bertrand a une longue entrée de blog sur les dates ( ici ), où il discute de ceci et d'autres questions de date.

Gordon Linoff
la source
15
Par souci d'exhaustivité sur cette bonne réponse, je suggérerais d'utiliser dateaddpour obtenir le lendemain.
Tim Lehner
7
@TimLehner Pour une bonne réponse, j'aurais utilisé le format ISO-8601 de «20130501». Pour les non-américains avec dateformat dmy, vous obtenez ceci:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi - Je crois que le bit explicite utilise un intervalle fermé à une extrémité ( >=) et ouvert à l'autre ( <) combiné avec une vérification un jour après la date de fin spécifiée.
HABO
3
@scottb Personnellement, je trouverais ennuyeux d'avoir à convertir en datetimes chaque fois que je voulais afficher, exporter, importer ou écrire une classe avec un datetime. Je pense que SQL Server dispose de nombreuses fonctionnalités intégrées pour manipuler et comparer les dates-heures dans la plupart des cas.
Tim Lehner
10
Vous ne devez jamais convertir une colonne dans la whereclause, car elle perdra toute indexation dont elle dispose. C'est un très mauvais modèle.
Buzinas
49

On a supposé que la deuxième référence de date dans la BETWEENsyntaxe était considérée comme par magie comme la «fin de la journée», mais ce n'est pas vrai .

c'est-à-dire que c'était prévu:

SELECT * FROM Cas 
O created_at ENTRE le début du '2013-05-01' ET la fin du '2013-05-01'

mais ce qui se passe vraiment est ceci:

SELECT * FROM Cas 
WHERE created_at ENTRE '2013-05-01 00: 00: 00 + 00000 ' ET '2013-05-01 00: 00: 00 + 00000 '

Ce qui devient l'équivalent de:

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

Le problème est l' une des perceptions / attentes au sujet BETWEENqui ne comprennent à la fois la valeur inférieure et les valeurs supérieures de la gamme, mais ne pas faire par magie une date de « début de » ou « la fin de ».

BETWEEN doit être évité lors du filtrage par plages de dates.

Utilisez toujours le à la >= AND <place

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

les parenthèses sont facultatives ici mais peuvent être importantes dans les requêtes plus complexes.

Used_By_Already
la source
2
Je ne suis pas
sûr
5
Aux éditeurs; N'essayez pas de transformer le pseudo SQL en blocs de code, cela ne fonctionne tout simplement pas car le commentaire repose sur BOLD.
Used_By_Already
2
Aux éditeurs (encore une fois), veuillez ne pas ajouter de faux guillemets dans le pseudo-code; ils n'aident PAS à la compréhension.
Used_By_Already
18

Vous devez effectuer l'une de ces deux options:

  1. Incluez la composante temps dans votre betweencondition: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(non recommandé ... voir le dernier paragraphe)
  2. Utilisez des inégalités au lieu de between. Notez qu'alors vous devrez ajouter un jour à la deuxième valeur:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Ma préférence personnelle est la deuxième option. De plus, Aaron Bertrand a une explication très claire sur les raisons pour lesquelles il devrait être utilisé.

Barranka
la source
6
+1 pour 2. Mais -1 pour 1. Ce hack de fin de journée est complètement peu fiable et une mauvaise idée.
Aaron Bertrand
1
@AaronBertrand Je préfère aussi l'option 2 (je l'utilise fréquemment). Mais pourquoi dites-vous que l'option 1 est «complètement non fiable»?
Barranka
5
veuillez lire ceci dans son intégralité . Vous ne devez jamais utiliser BETWEENpour les requêtes de plage de dates qui incluent l'heure; trop de choses peuvent mal tourner.
Aaron Bertrand
7

Utilisez simplement l'horodatage comme date:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
la source
4
La question est étiquetée SQL Server et 'DATE' is not a recognized built-in function name.doit convertir stackoverflow.com/a/20973452/4233593
Jeff Puckett
7

Je trouve que la meilleure solution pour comparer un champ datetime à un champ date est la suivante:

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 

Cela équivaut à une instruction inclusive entre car elle inclut à la fois la date de début et de fin ainsi que celles qui se situent entre les deux.

Ted
la source
3
cast(created_at as date)

Cela ne fonctionnera qu'en 2008 et dans les versions plus récentes de SQL Server

Si vous utilisez une version plus ancienne, utilisez

convert(varchar, created_at, 101)
Ali Adravi
la source
2
convert(varchar, created_at, 101)le résultat est comme dd/MM/yyyyet les chaînes de OP sont yyyy-MM-dd, je pense que cette réponse ne fonctionnera pas;).
shA.t
2

Vous pouvez utiliser la date()fonction qui extraira la date d'une date / heure et vous donnera le résultat comme date inclusive:

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

Date Dyamic BETWEEN requête 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
la source