Измислянето на начин за използване на BETWEEN с таблицата, каквато е, ще работи, но във всеки случай ще има по-лоша производителност:
- В най-добрия случай ще изразходва повече CPU, за да направи някакъв вид изчисление на редовете, вместо да работи с тях като дати.
- В най-лошия случай ще принуди сканиране на таблица на всеки ред в таблицата, но ако вашите колони имат индекси, тогава с правилната заявка е възможно търсене. Това може да е ОГРОМНА разлика в производителността, защото налагането на ограниченията в клауза BETWEEN ще деактивира използването на индекса.
Предлагам следното вместо това, ако имате индекс на вашите колони с дати и изобщо не ви пука за ефективността:
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)
)
);
Разбираемо е обаче, че не искате да използвате такава конструкция, тъй като е много неудобна. Така че тук е компромисна заявка, която поне използва числово изчисление и ще използва по-малко CPU от изчислението за преобразуване на дата в низ (макар и не достатъчно, за да компенсира принудителното сканиране, което е истинският проблем с производителността).
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;
Ако имате индекс на Year
, можете да получите голям тласък, като изпратите заявката по следния начин, която има възможност да търси:
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]
Въпреки че това нарушава изискването ви за използване на един BETWEEN
израз, не е много по-болезнено и ще се представи много добре с Year
индекс.
Можете също да промените масата си. Честно казано, използването на отделни числа за вашите части от датата вместо една колона с тип данни за дата не е добро. Причината, поради която не е добре, е точният проблем, пред който сте изправени в момента – много е трудно да се направи запитване.
В някои сценарии за съхранение на данни, където запазването на байтове има голямо значение, мога да си представя ситуации, при които можете да съхранявате датата като число (като 201111
), но това не се препоръчва. Най-добрият решението е да промените таблицата си, за да използвате дати, вместо да разделяте числовата стойност на месеца и годината. Просто запазете първия ден от месеца, като разпознавате, че той остава за целия месец.
Ако промяната на начина, по който използвате тези колони, не е опция, но все пак можете да промените таблицата си, тогава можете да добавите постоянна изчислена колона:
ALTER Table dbo.YourTable
ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
PERSISTED;
С това можете просто да направите:
SELECT *
FROM dbo.YourTable
WHERE
ActualDate BETWEEN '20111101' AND '20120201';
PERSISTED
ключова дума означава, че въпреки че все още ще получите сканиране, то няма да трябва да прави изчисления на всеки ред, тъй като изразът се изчислява на всяко INSERT или UPDATE и се съхранява в реда. Но вие можете получите търсене, ако добавите индекс към тази колона, което ще я направи много добра (въпреки че като цяло това все още не е толкова идеално, колкото промяната да използвате действителна колона с дата, защото ще отнеме повече място и ще засегне INSERT и АКТУАЛИЗАЦИИ):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
Резюме:ако наистина не можете да промените таблицата по никакъв начин, тогава ще трябва да направите компромис по някакъв начин. Няма да е възможно да получите простия синтаксис, който искате, който също ще работи добре, когато вашите дати се съхраняват разделени в отделни колони.