Наткнулась как-то в сообществе ru_perl на календарь на текущий месяц, выводимый одним SQL запросом. Очень понравилось, хочу поделиться (:
Тут я приведу выкладки из самого поста, надеюсь, копирайты указывать не надо. Ну и добавлю краткие сведения об операторах и функциях.
Сначала навыбираем циферок с запасом:
SELECT n, n +7, n +14, n +21, n +28, n +35 FROM ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 ) AS t;
+---+-----+------+------+------+------+ | n | n+7 | n+14 | n+21 | n+28 | n+35 | +---+-----+------+------+------+------+ | 1 | 8 | 15 | 22 | 29 | 36 | | 2 | 9 | 16 | 23 | 30 | 37 | | 3 | 10 | 17 | 24 | 31 | 38 | | 4 | 11 | 18 | 25 | 32 | 39 | | 5 | 12 | 19 | 26 | 33 | 40 | | 6 | 13 | 20 | 27 | 34 | 41 | | 7 | 14 | 21 | 28 | 35 | 42 | +---+-----+------+------+------+------+
Оператор UNION используется для объединения работы нескольких SELECTов. Имя или псевдоним колонки, запрошенный в первом SELECT используется потом для выводимого результата. Типы данных в каждом из запросов должны совпадать, в противном случае в зависимости от версии масика либо подбирается больший, либо обрезаются остальные. ALL означает, что будут выбраны все записи, а не только уникальные. В нашем запросе это не обязательно.
Также тут используется именование подзапроса (as t) после FROM. Это обязательно, т.к. все таблицы во FROM-клаузе должны быть поименованы. У меня этот кусок запроса временами глючил на локально установленном масике (через phpmyadmin), выдавая #1146 - Table 'mysql.t' doesn't exist.
Вернемся к календарю. Ограничим число дней в последнем столбце. Для этого нам надо знать число дней в месяце SELECT DAYOFMONTH(LAST_DAY(NOW())).
NOW выдает сегодняшнюю дату, LAST_DAY возвращает последнее число месяца для заданной даты в виде YYYY-MM-DD, DAYOFMONTH дает номер дня в месяце.
SELECT n, n+7, n+14, n+21, CASE WHEN n+28 > ld.maxn THEN NULL ELSE n +28 END AS 'n+28', CASE WHEN n+35 > ld.maxn THEN NULL ELSE n +35 END AS 'n+35' FROM ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 ) AS t, ( SELECT DAYOFMONTH(LAST_DAY(NOW( ))) AS maxn ) AS ld;
+---+-----+------+------+------+------+ | n | n+7 | n+14 | n+21 | n+28 | n+35 | +---+-----+------+------+------+------+ | 1 | 8 | 15 | 22 | NULL | NULL | | 2 | 9 | 16 | 23 | NULL | NULL | | 3 | 10 | 17 | 24 | NULL | NULL | | 4 | 11 | 18 | 25 | NULL | NULL | | 5 | 12 | 19 | 26 | NULL | NULL | | 6 | 13 | 20 | 27 | NULL | NULL | | 7 | 14 | 21 | 28 | NULL | NULL | +---+-----+------+------+------+------+
Осталось нашаманить, чтобы месяц не всегда начинался с понедельника. Вот так мы узнаем день недели первого дня, с учетом буржуазного обычая функции DAYOFWEEK начинать неделю с воскресенья:
SELECT CASE WHEN DAYOFWEEK( DATE_FORMAT( NOW( ) , '%Y-%m-01' ) ) =1 THEN 7 ELSE DAYOFWEEK( DATE_FORMAT( NOW( ) , '%Y-%m-01' ) )-1 END AS fn;
Собираем вместе:
SELECT CASE WHEN n-fn >0 THEN n-fn ELSE NULL END AS week1, n+7-fn AS week2, n+14-fn AS week3, n+21-fn AS week4, CASE WHEN n-fn+28 > ld.maxn THEN NULL ELSE n-fn+28 END AS week5, CASE WHEN n-fn+35 > ld.maxn THEN NULL ELSE n-fn+35 END AS week6 FROM ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 ) AS t, ( SELECT DAYOFMONTH(LAST_DAY(NOW( ))) AS maxn ) AS ld, ( SELECT CASE WHEN DAYOFWEEK( DATE_FORMAT( NOW( ) , '%Y-%m-01' ) )=1 THEN 6 ELSE DAYOFWEEK( DATE_FORMAT( NOW( ), '%Y-%m-01' ) )-2 END AS fn ) AS fd;
И получаем желанный результат:
+-------+-------+-------+-------+-------+-------+ | week1 | week2 | week3 | week4 | week5 | week6 | +-------+-------+-------+-------+-------+-------+ | NULL | 2 | 9 | 16 | 23 | NULL | | NULL | 3 | 10 | 17 | 24 | NULL | | NULL | 4 | 11 | 18 | 25 | NULL | | NULL | 5 | 12 | 19 | 26 | NULL | | NULL | 6 | 13 | 20 | 27 | NULL | | NULL | 7 | 14 | 21 | 28 | NULL | | 1 | 8 | 15 | 22 | NULL | NULL | +-------+-------+-------+-------+-------+-------+
Комментарии
Хорошая статья!!! А вот mdma это не круто)