Я ищу способ получить все подписки на вакансии за последние 12 месяцев. Поскольку не каждая вакансия будет иметь подписку в каждом месяце, некоторым строкам придется возвращать 0, но сейчас это просто не в результирующем наборе. Как добавить недостающие строки?
Я использую следующие таблицы:
CREATE TABLE 'calendar_months' (
'month_id' int(8) DEFAULT NULL,
'en_abbr' varchar(255) NOT NULL,
'en_long' varchar(255) NOT NULL,
'nl_abbr' varchar(255) NOT NULL,
'nl_long' varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO 'calendar_months' ('month_id', 'en_abbr', 'en_long', 'nl_abbr', 'nl_long') VALUES
(1, 'jan', 'January', 'jan', 'Januari'),
(2, 'feb', 'February', 'feb', 'Februari'),
(3, 'mar', 'March', 'mrt', 'Maart'),
(4, 'apr', 'April', 'apr', 'April'),
(5, 'may', 'May', 'mei', 'Mei'),
(6, 'jun', 'June', 'jun', 'Juni'),
(7, 'jul', 'July', 'jul', 'Juli'),
(8, 'aug', 'August', 'aug', 'Augustus'),
(9, 'sep', 'September', 'sep', 'September'),
(10, 'oct', 'October', 'okt', 'Oktober'),
(11, 'nov', 'November', 'nov', 'November'),
(12, 'dec', 'December', 'dec', 'December');
CREATE TABLE 'vacancies' (
'vacancy_id' int(11) NOT NULL,
'org_id' int(11) NOT NULL,
'name' varchar(255) NOT NULL COMMENT 'title',
'description' varchar(255) DEFAULT NULL,
'create_time' datetime DEFAULT NULL,
'is_deleted' tinyint(4) NOT NULL DEFAULT '0',
'status' int(11) NOT NULL DEFAULT '0'COMMENT
) ;
CREATE TABLE 'vacancy_subscriptions' (
'subscription_id' int(11) NOT NULL,
'vacancy_id' int(11) DEFAULT NULL,
'user_id' int(10) DEFAULT NULL,
'subscription_date' datetime NOT NULL,
'message' text
)
Я использую следующий запрос:
SELECT
CONCAT(cm.nl_long, ' ', YEAR(v.create_time)) as label, YEAR(v.create_time) as vacyear, MONTH(v.create_time) as vacmonth, COUNT(*) as totalsubscriptions
FROM 'calendar_months' as cm
LEFT JOIN 'vacancies' as v on cm.month_id = month(v.create_time)
LEFT JOIN 'vacancy_subscriptions' as vs on v.vacancy_id = vs.vacancy_id
WHERE
(v.create_time >= (DATE_ADD(NOW(),INTERVAL -12 MONTH)))
AND v.is_deleted = 0
AND v.org_id = 1
GROUP BY vacyear, vacmonth
ORDER BY vacyear ASC, vacmonth ASC
И это дает мне результат, который я ищу.
+---------------+---------+----------+--------------------+
| label | vacyear | vacmonth | totalsubscriptions |
+---------------+---------+----------+--------------------+
| Oktober 2017 | 2017 | 10 | 5 |
| November 2017 | 2017 | 11 | 1 |
| December 2017 | 2017 | 12 | 13 |
| Maart 2018 | 2018 | 3 | 4 |
| April 2018 | 2018 | 4 | 5 |
+---------------+---------+----------+--------------------+
Но как мне добавить месяцы без подписки за последние 12 месяцев, когда последний столбец просто "0"? Как это:
+----------------+---------+----------+--------------------+
| label | vacyear | vacmonth | totalsubscriptions |
+----------------+---------+----------+--------------------+
| Mei 2017 | 2017 | 5 | 0 |
| Juni 2017 | 2017 | 6 | 0 |
| Juli 2017 | 2017 | 7 | 0 |
| Augustus 2017 | 2017 | 8 | 0 |
| September 2017 | 2017 | 9 | 0 |
| Oktober 2017 | 2017 | 10 | 5 |
| November 2017 | 2017 | 11 | 1 |
| December 2017 | 2017 | 12 | 13 |
| Januari 2018 | 2018 | 1 | 0 |
| Februari 2018 | 2018 | 2 | 0 |
| Maart 2018 | 2018 | 3 | 4 |
| April 2018 | 2018 | 4 | 5 |
+----------------+---------+----------+--------------------+
UPDATE: я создал скрипт SQL, содержащий некоторые данные! http://www.sqlfiddle.com/#!9/75db76
Переместите свое условие, связанное с левой таблицей join в позиции on
SELECT
CONCAT(cm.nl_long, ' ', YEAR(v.create_time)) as label, YEAR(v.create_time) as vacyear, ifnull(MONTH(v.create_time),0) as vacmonth, ifnull(COUNT(*),0) as totalsubscriptions
FROM 'calendar_months' as cm
LEFT JOIN 'vacancies' as v on cm.month_id = month(v.create_time)
AND (v.create_time >= (DATE_ADD(NOW(),INTERVAL -12 MONTH)))
AND v.is_deleted = 0
AND v.org_id = 1
LEFT JOIN 'vacancy_subscriptions' as vs on v.vacancy_id = vs.vacancy_id
GROUP BY vacyear, vacmonth, label
ORDER BY vacyear ASC, vacmonth ASC
в противном случае условие where работает как внутреннее соединение и не возвращает строки, если значение не соответствует
сделайте то, что у вас нет года за весь месяц, когда вы можете использовать крест-соединение в союзе для получения этого значения
SELECT
CONCAT(cm.nl_long, ' ', y.my_year) as label, YEAR(v.create_time) as vacyear
, ifnull(MONTH(v.create_time), 0) as vacmonth, ifnull(COUNT(*),0) as totalsubscriptions
FROM 'calendar_months' as cm
CROSS JOIN (
select 2018 my_year
from dual
union
select 2017
from dual
) y
LEFT JOIN 'vacancies' as v on cm.month_id = month(v.create_time)
AND (v.create_time >= (DATE_ADD(NOW(),INTERVAL -12 MONTH)))
AND v.is_deleted = 0
AND v.org_id = 1
LEFT JOIN 'vacancy_subscriptions' as vs on v.vacancy_id = vs.vacancy_id
GROUP BY vacyear, vacmonth, label
ORDER BY y.my_year ASC, vacmonth ASC
Ваша логика нарушена. Ваша таблица календаря должна включать как месяц, так и год - и первую дату месяца (для удобства).
Тогда вы можете сделать:
SELECT CONCAT(cm.nl_long, ' ', cm.year) as label, COUNT(*) as totalsubscriptions
FROM calendar_months cm LEFT JOIN
vacancies v
ON cm.month_id = month(v.create_time) AND
cm.year = year(v.create_time) AND
v.is_deleted = 0 AND
v.org_id = 1 LEFT JOIN
'vacancy_subscriptions' vs
on v.vacancy_id = vs.vacancy_id
WHERE cm.month_start_date >= DATE_ADD(NOW(), INTERVAL -12 MONTH))
GROUP BY label
ORDER BY MIN(cm.month_start_date);
Заметки:
ON
(для LEFT JOIN
.GROUP BY
должна быть в полях в первой таблице.NOW()
- вы получите 13 месяцев данных с частичным первым и последним месяцами.