Придумать способ использования BETWEEN с таблицей как она есть, будет работать, но в любом случае будет хуже с точки зрения производительности:
- В лучшем случае будет потребляться больше ЦП, чтобы выполнять какие-то вычисления в строках, а не работать с ними как с датами.
- В худшем случае это вызовет сканирование таблицы для каждой строки в таблице, но если ваши столбцы имеют индексы, то при правильном запросе возможен поиск. Это может быть ОГРОМНОЙ разницей в производительности, потому что принудительное включение ограничений в предложение 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)
)
);
Однако понятно, что вы не хотите использовать такую конструкцию, так как она очень неудобна. Итак, вот компромиссный запрос, который, по крайней мере, использует числовые вычисления и будет использовать меньше ЦП, чем вычисление преобразования даты в строку (хотя и недостаточно меньше, чтобы компенсировать принудительное сканирование, которое является реальной проблемой производительности).
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 и UPDATE):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
Резюме: если вы действительно не можете изменить таблицу каким-либо образом, вам придется каким-то образом пойти на компромисс. Невозможно получить простой синтаксис, который вам нужен, который также будет хорошо работать, когда ваши даты хранятся в отдельных столбцах.
20.12.2012