Как стать автором
Обновить

Микрозаметка: Итераторы/Генерация диапазонов дат, чисел и тд

Время на прочтение3 мин
Количество просмотров1.7K
Эта заметка навеяна топиком "подсчет количества событий календаря в каждом месяце года". В ней нет ничего нового, это просто микрозаметка о возможных решениях.
Хотя задача того топика очень типична и вполне спокойно решалась обычным проходом с case или if:
SELECT
sum(
 CASE
  when t.`start_date`<'2010-02-01' and t.end_date>'2010-01-01'   then 1
  else 0
 end
)
AS jan,
sum(
 CASE
  when t.`start_date`<'2010-03-01' and t.end_date>'2010-02-01'   then 1
  else 0
 end
)
AS feb,
...
FROM test t


Но я счел нужным написать о некоторых возможностях избежать излишнюю ручную работу. Например, если нам необходимо бы было агрегировать не за год и не за два, а, скажем, за последние 5 лет помесячно. Согласитесь, в таком случае 60 строк c if'ами было бы как минимум тяжело читать.

JayDi шел в правильном направлении говоря о табличке для дат, но ее бы пришлось отдельно создавать да еще и с конкретными условиями(подневно, помесячно или посекундно и тд)… Этот способ даже используют иногда.

В принципе я не имею ничего против создания temporary tables, но в данном случае они совершенно излишни. В Oracle, например, генерацию таких временных диапазонов-счетчиков можно сделать как угодно, например, "select rownum from dual connect by rownum<N", или создав pipelined-функцию, или используя типы из dbms_sql, или используя коллекции. В mysql, к сожалению, нет «connect by», но есть information_schema — аналог ораклового data dictionary, а ораклисты довольно часто используют для тестовых прогонов запросы вида "select rownum from all_objects". Таким же образом можем поступить и мы:

Выборка для группировки и джойна для 10 месяцев с начала 2010:
--set @rownumber:=0;
select
     case
                 when @rownumber is null
                 then @rownumber:=1
                 else @rownumber:=@rownumber+1
                 end n,
     DATE_FORMAT(
                 date_add('2010-01-01', interval @rownumber month),
                 '%Y.%m') month
from
information_schema.columns t
limit 10


* This source code was highlighted with Source Code Highlighter.

Здесь используется просто в качестве генератора строк табличка information_schema.column, которую можно заменить на любую другую, которая в основном не используется и в которой гарантированно достаточно строк для нашего запроса(в моем случае, например, их 5281). Первая закомментированная строка должна быть выполнена для обнуления переменной-счетчика.
Получим:
n	month
1	2010.01
2	2010.02
3	2010.03
4	2010.04
5	2010.05
6	2010.06
7	2010.07
8	2010.08
9	2010.09
10	2010.10

Теперь эту таблицу вы можете сджойнить с вашей таблицей по вашим условиям. В случае, если заранее не знаете необходимого кол-ва месяцев(строк), то вместо limit ограничьте условиями минимальной и максимальной даты.

Другой вариант решения: решение «в лоб» — использование курсоров. Ну, что может быть логичнее, чем создать процедуру с использованием курсоров в случае, если нам необходимо собрать более сложные аггрегаты, чем встроенные? Этот вопрос я даже не буду описывать(можете почитать, например, тут), но не забывайте про них(в том топике никто об этом даже не вспомнил...)
Теги:
Хабы:
Всего голосов 33: ↑29 и ↓4+25
Комментарии15

Публикации