Wymyślenie sposobu na użycie BETWEEN z tabelą taką, jaka jest, będzie działać, ale w każdym przypadku będzie gorsza:
- W najlepszym wypadku zużyje więcej procesora, aby wykonać jakieś obliczenia na wierszach, zamiast pracować z nimi jako datami.
- W najgorszym przypadku wymusi to skanowanie każdego wiersza w tabeli, ale jeśli Twoje kolumny mają indeksy, to przy odpowiednim zapytaniu możliwe jest wyszukiwanie. Może to być OGROMNA różnica w wydajności, ponieważ wymuszenie ograniczeń w klauzuli BETWEEN spowoduje wyłączenie korzystania z indeksu.
Zamiast tego proponuję następujące rozwiązanie, jeśli masz indeks w swoich kolumnach dat i w ogóle zależy Ci na wydajności:
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)
)
);
Jednak zrozumiałe jest, że nie chcesz używać takiej konstrukcji, ponieważ jest to bardzo niewygodne. Więc tutaj jest zapytanie kompromisowe, które przynajmniej używa obliczeń numerycznych i będzie zużywało mniej procesora niż obliczenia konwersji daty na ciąg (choć nie wystarczająco mniej, aby zrekompensować wymuszone skanowanie, które jest prawdziwym problemem z wydajnością).
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;
Jeśli masz indeks na Year
, możesz uzyskać duży wzrost, przesyłając zapytanie w następujący sposób, które ma możliwość wyszukiwania:
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]
Chociaż to łamie Twój wymóg używania jednego BETWEEN
wyrażenie, nie jest zbyt bolesne i będzie działać bardzo dobrze z Year
indeks.
Możesz także zmienić swój stół. Szczerze mówiąc, używanie oddzielnych liczb dla części dat zamiast pojedynczej kolumny z typem danych daty nie jest dobre. Powodem, dla którego nie jest to dobre, jest dokładny problem, z którym się teraz borykasz - bardzo trudno jest zapytać.
W niektórych scenariuszach hurtowni danych, w których zapisywanie bajtów ma duże znaczenie, mógłbym wyobrazić sobie sytuacje, w których można przechowywać datę jako liczbę (np. 201111
), ale nie jest to zalecane. najlepsze rozwiązaniem jest zmiana tabeli tak, aby używała dat zamiast dzielenia wartości liczbowej miesiąca i roku. Po prostu zapisz pierwszy dzień miesiąca, uznając, że jest to cały miesiąc.
Jeśli zmiana sposobu korzystania z tych kolumn nie jest możliwa, ale nadal możesz zmienić swoją tabelę, możesz dodać utrwaloną kolumnę obliczaną:
ALTER Table dbo.YourTable
ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
PERSISTED;
Dzięki temu możesz po prostu zrobić:
SELECT *
FROM dbo.YourTable
WHERE
ActualDate BETWEEN '20111101' AND '20120201';
PERSISTED
słowo kluczowe oznacza, że chociaż nadal będziesz otrzymywać skanowanie, nie będziesz musiał wykonywać żadnych obliczeń w każdym wierszu, ponieważ wyrażenie jest obliczane przy każdym INSERT lub UPDATE i przechowywane w wierszu. Ale możesz uzyskaj wyszukiwanie, jeśli dodasz indeks do tej kolumny, co sprawi, że będzie działała bardzo dobrze (chociaż w sumie nie jest to tak idealne, jak zmiana na użycie rzeczywistej kolumny daty, ponieważ zajmie więcej miejsca i wpłynie na INSERT i AKTUALIZACJE):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
Podsumowanie:jeśli naprawdę nie możesz w żaden sposób zmienić tabeli, będziesz musiał w jakiś sposób pójść na kompromis. Nie będzie możliwe uzyskanie prostej składni, która będzie również działać dobrze, gdy daty są przechowywane w osobnych kolumnach.