Принятый ответ минимизирует количество обрабатываемых строк.
where LAST_DAY(start) between start and end
Это, однако, все равно будет обрабатывать строки за пределами года, который вы хотите. Таким образом, вы все равно должны использовать граничные условия. Такие как ...
where LAST_DAY(start) between start and end
and start between '2018-01-01' and '2018-12-31'
У него все еще есть недостаток: ни один индекс не может удовлетворить условию LAST_DAY()
, и произойдет сканирование таблицы или индекса.
С помощью информации, предоставленной вами в комментариях, использование поиска по индексу может быть восстановлено.
Каждая строка всегда на неделю и начинается в понедельник. Таким образом, для каждого конкретного конца месяца, если мы вернемся к понедельнику, мы получим start
, который соответствует.
WHERE
start IN (
DATE_TRUNC(WEEK, LAST_DAY('2018-01-01')), -- find last day in jan, round down to Monday
DATE_TRUNC(WEEK, LAST_DAY('2018-02-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-03-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-04-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-05-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-06-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-07-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-08-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-09-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-10-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-11-01')),
DATE_TRUNC(WEEK, LAST_DAY('2018-12-01'))
)
Это значительно дольше.
Это можно уменьшить с помощью таблицы календаря со списком дат и метаданными о тех датах, которые помогут вам выполнять быстрый поиск.
А пока создайте индекс для start
и попробуйте явное предложение where выше. Это должно быть даже быстрее, чем вы уже пробовали.
EDIT:
Точно так же вы могли бы написать свои CASE
выражения - это способ, который «легче читать человеку» ...
MAX(CASE WHEN start = DATE_TRUNC(WEEK, LAST_DAY('2018-01-01')) THEN 1 ELSE 0 END) AS M01