2014-10-07 32 views
15

Non riesco a determinare il modo migliore per confrontare le date in SQL in base al mese e all'anno.Confronto di data di SQL Server in base al mese e all'anno

Effettuiamo calcoli in base alle date e poiché la fatturazione si verifica su base mensile, la data del mese ha causato un maggiore ostacolo.

Ad esempio

DECLARE @date1 DATETIME = CAST('6/15/2014' AS DATETIME), 
     @date2 DATETIME = CAST('6/14/2014' AS DATETIME) 

SELECT * FROM tableName WHERE @date1 <= @date2 

L'esempio di cui sopra non sarebbe tornato alcuna riga dal @ date1 è maggiore di @ date2. Quindi mi piacerebbe trovare un modo per prendere il giorno fuori dall'equazione.

Analogamente, la seguente situazione mi dà dolore per lo stesso motivo.

DECLARE @date1 DATETIME = CAST('6/14/2014' AS DATETIME), 
     @date2 DATETIME = CAST('6/15/2014' AS DATETIME), 
     @date3 DATETIME = CAST('7/1/2014' AS DATETIME) 

SELECT * FROM tableName WHERE @date2 BETWEEN @date1 AND @date3 

ho fatto conversioni in linea delle date per ricavare il primo giorno e l'ultimo giorno del mese per la data specificata.

SELECT * 
FROM tableName 
WHERE date2 BETWEEN 
    DATEADD(month, DATEDIFF(month, 0, date1), 0) -- The first day of the month for date1 
    AND 
    DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, date2) + 1, 0)) -- The lastday of the month for date3 

Ci deve essere un modo più semplice per farlo. Eventuali suggerimenti?

+1

Quale versione di SQL Server? – christiandev

+0

@christiandev Stiamo usando SQL 2008. –

+1

Come nota a margine, se non si utilizza la parte dell'ora, è possibile utilizzare il tipo di dati 'DATE'. – christiandev

risposta

15

Per gestire le disuguaglianze, ad esempio tra, mi piace convertire la data/ora in una rappresentazione YYYYMM, sia come stringa o come numero intero. Per questo esempio:

DECLARE @date1 DATETIME = CAST('6/14/2014' AS DATETIME), 
     @date2 DATETIME = CAST('6/15/2014' AS DATETIME), 
     @date3 DATETIME = CAST('7/1/2014' AS DATETIME); 

SELECT * FROM tableName WHERE @date2 BETWEEN @date1 AND @date3; 

vorrei scrivere la query come:

SELECT * 
FROM tableName 
WHERE year(@date2) * 100 + month(@date2) BETWEEN year(@date1) * 100 + month(@date1) AND 
               year(@date3) * 100 + month(@date1); 
6

È possibile aderire on MONTH e YEAR valori di queste date:

SELECT * 
FROM tableName 
WHERE YEAR(@date1) = YEAR(@date2) AND MONTH(@date1) = MONTH(@date2) 
+2

+1 mi hai battuto di pochi secondi :-) – Tanner

+0

Grazie per la risposta, ma che non affronta gli intervalli di date. Ad esempio 'WHERE date1 BETWEEN date2 AND date3' –

+1

@AndyEvans Rilasciato il mio edit, ripensandoci per intervalli meglio usare la risposta di GordonLinoff. – DavidG

13

è possibile filtrare il mese e l'anno di una certa data alla data corrente in questo modo:

SELECT * 
FROM tableName 
WHERE month(date2) = month(getdate()) and year(date2) = year(getdate()) 

basta sostituire il metodo GETDATE() con la data desiderata.

+1

Ti meriti ancora +1 anche se :) – DavidG

+0

Grazie per la risposta, ma che non risolve gli intervalli di date. Ad esempio 'DOVE data1 TRA data2 AND data3' –

+1

@AndyEvans quindi vuoi tutti i record in cui la data cade in un solo mese di un anno? – Tanner

8

In primo luogo, mi piacerebbe utilizzare un formato per le date che è inequivocabile, come lo standard 'YYYYMMDD' e non l'hai '6/15/2014' stato usando. blog di Aaron Bertrand spiega molto meglio di quanto ho potuto, i vari modi in cui questo può andare storto:
Bad habits to kick : mis-handling date/range queries

Per il problema specifico, l'ultima ricerca che trova il primo e gli ultimi giorni dei mesi (per date1 e data 3), secondo me è sulla strada giusta. Hai solo bisogno se i primi giorni del mese (prima giornata di date1 e primo giorno del prossimo mese per data3), se si evita l'male BETWEEN: What do BETWEEN and the devil have in common?

SELECT * 
FROM tableName 
WHERE date2 >= DATEADD(month, DATEDIFF(month, '19000101', @date1), '19000101') 
    AND date2 < DATEADD(month, 1+DATEDIFF(month, '19000101', @date3), '19000101') ; 

La query funziona così com'è, non importa il tipo di dati di date2 (DATE, DATETIME, DATETIME2 o SMALLDATTEIME).

Il punto bonus, gli indici su date2 verranno considerati dall'ottimizzatore in questo modo.


Improvement, secondo (ancora, un altro) post sul blog di Aaron, per evitare un problema con la stima di cardinalità quando si valutano le espressioni con DATEDIFF():
Performance Surprises and Assumptions : DATEDIFF

SELECT * 
FROM tableName 
WHERE date2 >= CONVERT(DATE, DATEADD(day, 1 - DAY(@date1), @date1)) 
    AND date2 < DATEADD(month, 1, 
         CONVERT(DATE, DATEADD(day, 1 - DAY(@date3), @date3))) ; 
Problemi correlati