Постановка задачи:
вывести количество событий (events) каждого месяца года.
Каждое событие имеет два поля
— start_date — дата начала события
— end_date — дата завершения события
Структура таблички с событиями календаря:
Ожидаемый результат:
Возможные варианты:
— MONTH(start_date) = MONTH(end_date), YEAR(start_date) = YEAR(end_date);
Событие начинается и заканчивается в и том же месяце одного года.
— MONTH(start_date) < MONTH(end_date), YEAR(start_date) = YEAR(end_date);
Событие начинается в одном месяце, а заканчивается в другом месяце одного года.
— MONTH(start_date) = MONTH(end_date), YEAR(start_date) < YEAR(end_date);
Событие начинается и заканчивается в одном и том же месяце но разных лет (просто длится год).
— MONTH(start_date) < MONTH(end_date), YEAR(start_date) < YEAR(end_date);
Событие начинается и заканчивается в разных месяцах разных лет (началось в декабре, закончилось в январе).
Решение:
Решить данную задачу можно двумя способами: вывести значения в строку (каждый столбец — месяц), либо в столбик (каждая строка — месяц с количеством событий).
Намучившись с JOINами было принято решение выводить результаты в строку (неограниченная длительность события стала проблеммой в использовании JOIN). То есть решили решать задачу в «лоб», ограничивать события по году и проверять в каких месяцах проходит данное событие. Для проверки вхождения была написана MYSQL function.
Реализация:
Эта функция на вход получает дату начала события, дату конца события, месяц и год, по которым необходимо выполнить проверку, и в качестве результата возвращает 0 либо 1. Иными словами, функция проверяет вхождение текущего месяца в интервал начала и конца события.
Использование:
Результат будет в строку, в каждом столбце количество событий в месяц конкретного года.
Резюмируя:
Реализация не притендует на элегантность, я думаю, что кому-нибудь станет полезной при реализации такой стандартной функциональности как календарь событий.
Вопросы и улучшения приветствуются.
PS. не ищите в табличке поля title и description — это переводимые поля и в данной таблице их нет.
UPD:
Пользователь xtender предложил более правильный вариант решения задачи.
вывести количество событий (events) каждого месяца года.
Каждое событие имеет два поля
— start_date — дата начала события
— end_date — дата завершения события
Структура таблички с событиями календаря:
CREATE TABLE `events` (
`id` int(11) unsigned NOT NULL auto_increment,
`start_date` date default NULL,
`end_date` date default NULL,
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
* This source code was highlighted with Source Code Highlighter.
Ожидаемый результат:
January | 21 | February | 34 | March | 47 |
May | 8 | June | 12 | July | 23 |
August | 56 | September | 11 | October | 35 |
November | 34 | Desember | 24 |
Возможные варианты:
— MONTH(start_date) = MONTH(end_date), YEAR(start_date) = YEAR(end_date);
Событие начинается и заканчивается в и том же месяце одного года.
— MONTH(start_date) < MONTH(end_date), YEAR(start_date) = YEAR(end_date);
Событие начинается в одном месяце, а заканчивается в другом месяце одного года.
— MONTH(start_date) = MONTH(end_date), YEAR(start_date) < YEAR(end_date);
Событие начинается и заканчивается в одном и том же месяце но разных лет (просто длится год).
— MONTH(start_date) < MONTH(end_date), YEAR(start_date) < YEAR(end_date);
Событие начинается и заканчивается в разных месяцах разных лет (началось в декабре, закончилось в январе).
Решение:
Решить данную задачу можно двумя способами: вывести значения в строку (каждый столбец — месяц), либо в столбик (каждая строка — месяц с количеством событий).
Намучившись с JOINами было принято решение выводить результаты в строку (неограниченная длительность события стала проблеммой в использовании JOIN). То есть решили решать задачу в «лоб», ограничивать события по году и проверять в каких месяцах проходит данное событие. Для проверки вхождения была написана MYSQL function.
Реализация:
DELIMITER $$
DROP FUNCTION IF EXISTS `isEventInMonth`$$
CREATE FUNCTION `isEventInMonth`(
startdate DATE,
enddate DATE,
_month INT(1),
_year INT(1)
) RETURNS INT(1)
BEGIN
DECLARE results INT(1);
IF YEAR(startdate) = YEAR(enddate) THEN
SET results = IF( MONTH(startdate) = _month
AND MONTH(enddate) = _month
OR
( MONTH(startdate) <> MONTH(enddate)
AND (MONTH(startdate) <= _month
AND MONTH(enddate) >= _month)
)
,1,0);
ELSEIF YEAR(startdate) = _year THEN
SET results = IF( MONTH(startdate) <= _month ,1,0);
ELSE
SET results = IF( MONTH(enddate) >= _month ,1,0);
END IF;
RETURN results;
END$$
DELIMITER ;
* This source code was highlighted with Source Code Highlighter.
Эта функция на вход получает дату начала события, дату конца события, месяц и год, по которым необходимо выполнить проверку, и в качестве результата возвращает 0 либо 1. Иными словами, функция проверяет вхождение текущего месяца в интервал начала и конца события.
Использование:
SELECT
SUM(isEventInMonth(start_date,end_date,1,2011)) AS jan,
SUM(isEventInMonth(start_date,end_date,2,2011)) AS feb,
SUM(isEventInMonth(start_date,end_date,3,2011)) AS mar,
SUM(isEventInMonth(start_date,end_date,4,2011)) AS apr,
SUM(isEventInMonth(start_date,end_date,5,2011)) AS may,
SUM(isEventInMonth(start_date,end_date,6,2011)) AS jun,
SUM(isEventInMonth(start_date,end_date,7,2011)) AS jul,
SUM(isEventInMonth(start_date,end_date,8,2011)) AS aug,
SUM(isEventInMonth(start_date,end_date,9,2011)) AS sep,
SUM(isEventInMonth(start_date,end_date,10,2011)) AS 'oct',
SUM(isEventInMonth(start_date,end_date,11,2011)) AS nov,
SUM(isEventInMonth(start_date,end_date,12,2011)) AS 'dec'
FROM EVENTS WHERE YEAR(start_date) = 2011 OR YEAR(end_date) = 2011
* This source code was highlighted with Source Code Highlighter.
Результат будет в строку, в каждом столбце количество событий в месяц конкретного года.
Резюмируя:
Реализация не притендует на элегантность, я думаю, что кому-нибудь станет полезной при реализации такой стандартной функциональности как календарь событий.
Вопросы и улучшения приветствуются.
PS. не ищите в табличке поля title и description — это переводимые поля и в данной таблице их нет.
UPD:
Пользователь xtender предложил более правильный вариант решения задачи.
SELECT<br>
sum(CASE when t.`start_date`<'2010-02-01' and t.end_date>='2010-01-01' then 1 else 0 end) AS jan,<br>
sum(CASE when t.`start_date`<'2010-03-01' and t.end_date>='2010-02-01' then 1 else 0 end) AS feb,<br>
sum(CASE when t.`start_date`<'2010-04-01' and t.end_date>='2010-03-01' then 1 else 0 end) AS mar,<br>
sum(CASE when t.`start_date`<'2010-05-01' and t.end_date>='2010-04-01' then 1 else 0 end) AS apr,<br>
sum(CASE when t.`start_date`<'2010-06-01' and t.end_date>='2010-05-01' then 1 else 0 end) AS may,<br>
sum(CASE when t.`start_date`<'2010-07-01' and t.end_date>='2010-06-01' then 1 else 0 end) AS jun,<br>
sum(CASE when t.`start_date`<'2010-08-01' and t.end_date>='2010-07-01' then 1 else 0 end) AS jul,<br>
sum(CASE when t.`start_date`<'2010-09-01' and t.end_date>='2010-08-01' then 1 else 0 end) AS aug,<br>
sum(CASE when t.`start_date`<'2010-10-01' and t.end_date>='2010-09-01' then 1 else 0 end) AS sep,<br>
sum(CASE when t.`start_date`<'2010-11-01' and t.end_date>='2010-10-01' then 1 else 0 end) AS oct,<br>
sum(CASE when t.`start_date`<'2010-12-01' and t.end_date>='2010-11-01' then 1 else 0 end) AS nov,<br>
sum(CASE when t.`start_date`<'2011-01-01' and t.end_date>='2010-12-01' then 1 else 0 end) AS dec<br>
FROM events t<br>
<br>
* This source code was highlighted with Source Code Highlighter.