SQL "tra" non incluso


136

Ho una domanda come questa:

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

Ma questo non dà risultati anche se ci sono dati al 1 °.

created_atsembra 2013-05-01 22:25:19, sospetto che abbia a che fare con il tempo? Come potrebbe essere risolto?

Funziona bene se faccio intervalli di date più grandi, ma dovrebbe (inclusivo) funzionare anche con una sola data.


23
Bene, quanti numeri sono compresi tra 1 e 1? 1.5 dovrebbe essere compreso tra 1 e 1? Basta non usare TRA per gli intervalli di data / ora. Mai. E fai attenzione a come valuti "funziona bene" - hai esaminato attentamente i risultati dell'ultimo giorno dell'intervallo? Includeresti tutte le righe solo se a loro non era associato alcun tempo.
Aaron Bertrand,

URL aggiornato per Aaron: sqlblog.org/2011/10/19/…
JayRizzo,

Risposte:


296

Si è compreso. Stai confrontando i tempi dei dati con le date. La seconda data viene interpretata come mezzanotte quando inizia la giornata .

Un modo per risolvere questo problema è:

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

Un altro modo per risolverlo è con confronti binari espliciti

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

Aaron Bertrand ha un lungo post sul blog sulle date ( qui ), dove discute di questo e di altri problemi di data.


15
Per completezza su questa buona risposta, suggerirei di usare dateaddper ottenere il giorno successivo.
Tim Lehner,

7
@TimLehner Per una buona risposta, avrei usato il formato ISO-8601 di "20130501". Per i non statunitensi con dateformat dmy, ottieni questo:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi - Credo che il bit esplicito stia utilizzando un intervallo chiuso a un'estremità ( >=) e aperto all'altra ( <) combinato con il controllo di un giorno oltre la data di fine specificata.
HABO

3
@scottb Personalmente, troverei fastidioso dover convertire in orari ogni volta che volevo visualizzare, esportare, importare o scrivere una classe con un datetime. Penso che SQL Server abbia molte funzionalità integrate per manipolare e confrontare i tempi dei dati per la maggior parte degli scopi.
Tim Lehner,

10
Non devi mai lanciare una colonna nella whereclausola, poiché perderà qualsiasi indicizzazione che ha. È un modello davvero brutto.
Buzinas,

49

È stato ipotizzato che il riferimento alla seconda data nella BETWEENsintassi sia magicamente considerato la "fine della giornata", ma ciò non è vero .

cioè questo era previsto:

SELEZIONA * DA Casi 
DOVE creato_at TRA l'inizio di '2013-05-01' E la fine di '2013-05-01'

ma quello che succede davvero è questo:

SELEZIONA * DA Casi 
DOVE creato_at TRA '2013-05-01 00: 00: 00 + 00000 ' AND '2013-05-01 00: 00: 00 + 00000 '

Che diventa l'equivalente di:

SELEZIONA * DA Casi DOVE create_at = '2013-05-01 00: 00: 00 + 00000 '

Il problema è una delle percezioni / aspettative circa BETWEEN, che non includono sia il valore più basso ed i valori superiori della gamma, ma non magicamente fare una data "all'inizio di" o "alla fine di".

BETWEEN dovrebbe essere evitato quando si filtra per intervalli di date.

Usa sempre>= AND < invece

SELEZIONA * DA Casi 
WHERE (created_at > = '20.130.501' E created_at < '20.130.502')

le parentesi sono facoltative qui, ma possono essere importanti nelle query più complesse.


2
Non sono sicuro della saggezza di pubblicare questo post dopo che alla domanda è già stata data una risposta, ma volevo sottolineare un punto leggermente diverso
Used_By_Al già

5
Agli editori; per favore non provare a trasformare lo pseudo SQL in blocchi di codice, semplicemente non funziona poiché il commento si basa su BOLD.
Used_By_Al già

2
Per gli editori (di nuovo), per favore, non aggiungere virgolette spurie attraverso lo pseudo codice; NON aiutano la comprensione.
Used_By_ già il

18

Devi fare una di queste due opzioni:

  1. Includi il componente temporale nella tua betweencondizione: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(non consigliato ... vedi l'ultimo paragrafo)
  2. Usa le disuguaglianze invece di between. Si noti che quindi sarà necessario aggiungere un giorno al secondo valore:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

La mia preferenza personale è la seconda opzione. Inoltre, Aaron Bertrand ha una spiegazione molto chiara sul perché dovrebbe essere usato.


6
+1 per 2. Ma -1 per 1. Questo hack di fine giornata è completamente inaffidabile e una cattiva idea.
Aaron Bertrand,

1
@AaronBertrand Preferisco anche l'opzione 2 (la uso spesso). Ma perché dici che l'opzione 1 è "completamente inaffidabile"?
Barranka,

5
per favore leggi questo per intero . Non utilizzare mai BETWEENquery per intervalli di date che includono l'ora; troppo può andare storto.
Aaron Bertrand,


7

Trovo che la migliore soluzione per confrontare un campo datetime con un campo data sia la seguente:

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 

Ciò equivale a una dichiarazione tra inclusiva in quanto include sia la data di inizio e di fine, sia quelle comprese tra.


3
cast(created_at as date)

Funzionerà solo nel 2008 e nelle versioni più recenti di SQL Server

Se si utilizza una versione precedente, utilizzare

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)il risultato è simile dd/MM/yyyye le stringhe di OP lo sono yyyy-MM-dd, penso che questa risposta non funzionerà;).
shA.t

2

Puoi usare la date()funzione che estrarrà la data da un datetime e ti darà il risultato come data inclusiva:

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

0

Data dinamica TRA query 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]);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.