Trovare un modo per utilizzare BETWEEN con la tabella così com'è funzionerà, ma in ogni caso le prestazioni saranno peggiori:
- Nel migliore dei casi consumerà più CPU per eseguire qualche tipo di calcolo sulle righe invece di utilizzarle come date.
- Nel peggiore dei casi forzerà una scansione della tabella su ogni riga della tabella, ma se le tue colonne hanno indici, allora con la query giusta è possibile una ricerca. Questa potrebbe essere una ENORME differenza di prestazioni, perché forzare i vincoli in una clausola BETWEEN disabiliterà l'utilizzo dell'indice.
Suggerisco invece quanto segue se hai un indice nelle colonne della data e ti interessa per niente il rendimento:
DECLARE
@FromDate date = '20111101',
@ToDate date = '20120201';
SELECT *
FROM dbo.YourTable T
WHERE
(
T.[Year] > Year(@FromDate)
OR (
T.[Year] = Year(@FromDate)
AND T.[Month] >= Month(@FromDate)
)
) AND (
T.[Year] < Year(@ToDate)
OR (
T.[Year] = Year(@ToDate)
AND T.[Month] <= Month(@ToDate)
)
);
Tuttavia, è comprensibile che tu non voglia usare una tale costruzione in quanto è molto imbarazzante. Quindi ecco una query di compromesso, che utilizza almeno il calcolo numerico e utilizzerà meno CPU rispetto al calcolo della conversione da data a stringa (anche se non abbastanza per compensare la scansione forzata che è il vero problema di prestazioni).
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;
Se hai un indice su Year
, puoi ottenere una grande spinta inviando la query come segue, che ha l'opportunità di cercare:
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]
Anche se questo infrange la tua esigenza di utilizzare un singolo BETWEEN
espressione, non è molto più doloroso e funzionerà molto bene con l'Year
indice.
Puoi anche cambiare la tua tabella. Francamente, l'utilizzo di numeri separati per le parti della data invece di una singola colonna con un tipo di dati della data non va bene. Il motivo per cui non va bene è a causa del problema esatto che stai affrontando in questo momento:è molto difficile interrogare.
In alcuni scenari di data warehousing in cui il salvataggio di byte è molto importante, potrei immaginare situazioni in cui potresti memorizzare la data come un numero (come 201111
) ma non è raccomandato. Il migliore la soluzione è modificare la tabella per utilizzare le date invece di dividere il valore numerico del mese e dell'anno. Basta memorizzare il primo giorno del mese, riconoscendo che rimane valido per l'intero mese.
Se la modifica del modo in cui utilizzi queste colonne non è un'opzione ma puoi comunque modificare la tabella, puoi aggiungere una colonna calcolata persistente:
ALTER Table dbo.YourTable
ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
PERSISTED;
Con questo puoi semplicemente fare:
SELECT *
FROM dbo.YourTable
WHERE
ActualDate BETWEEN '20111101' AND '20120201';
Il PERSISTED
la parola chiave significa che mentre otterrai comunque una scansione, non dovrà eseguire alcun calcolo su ogni riga poiché l'espressione viene calcolata su ogni INSERT o UPDATE e memorizzata nella riga. Ma tu puoi ottieni una ricerca se aggiungi un indice su questa colonna, che lo farà funzionare molto bene (anche se tutto sommato, questo non è ancora l'ideale come cambiare per utilizzare una colonna della data effettiva, perché occuperà più spazio e influirà sugli INSERT e AGGIORNAMENTI):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
Riepilogo:se davvero non puoi cambiare la tabella in alcun modo, allora dovrai fare un compromesso in qualche modo. Non sarà possibile ottenere la semplice sintassi che desideri che funzioni anche bene, quando le tue date vengono archiviate divise in colonne separate.