Вернуть также пустые месяцы с нулевым значением за последние 12 месяцев.

0

У меня есть следующий SQL, который проверяет подписки за последние 12 месяцев. Проблема в том, что если подписки в течение определенного месяца нет, она не будет отображаться в результатах. Возьмем, например, следующую структуру для таблицы вакансий:

+------------+---------------------+--------+------------+--+--+
| vacancy_id |     create_time     | org_id | is_deleted |  |  |
+------------+---------------------+--------+------------+--+--+
|          1 | 2018-04-06 08:09:48 |      1 |          0 |  |  |
|          2 | 2018-02-06 08:09:48 |      1 |          0 |  |  |
|          3 | 2017-08-06 08:09:48 |      1 |          0 |  |  |
+------------+---------------------+--------+------------+--+--+

и подписки на такие вакансии:

+-----------+-------+------------+
| subscr_id |  msg  | vacancy_id |
+-----------+-------+------------+
|         1 | test  |          1 |
|         2 | test2 |          3 |
+-----------+-------+------------+

Теперь мой запрос таков:

SELECT
                               YEAR(v.create_time) as vacyear, MONTH(v.create_time) as vacmonth, COUNT(*) as totalsubscriptions
                            FROM 'vacancies' as v 
                            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

Что работает и дает правильный результат в случае, если есть подписка на этот месяц. В этом примере, как я могу позволить выводить все месяцы?

2018-04
2018-03
2018-02
2018-01
2017-12
2017-11
2017-10
2017-9
2017-8
2017-7
2017-6
2017-5

ОБНОВИТЬ:

Я попробовал решение, предложенное ниже пользователем @Gordon Linoff, и создал таблицу, содержащую month_id, с такими именами:

CREATE TABLE 'calendar_months' ( 'month_id' INT(8) NOT 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;

И я вставил данные там вот так:

month_id = 1
en_long = 'January'
...
month_id = 2
en_long = 'February'
...

Затем я изменил запрос, чтобы стать следующим:

SELECT
   YEAR(v.create_time) as vacyear, MONTH(v.create_time) as vacmonth, COUNT(*) as totalsubscriptions, cm.nl_long
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

Результат остается тем же

ОБНОВЛЕНИЕ 3:

SELECT
   YEAR(v.create_time) as vacyear, MONTH(v.create_time) as vacmonth, COUNT(*) as totalsubscriptions
FROM 'vacancies' as v 
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

Этот запрос дает правильные результаты: Изображение 174551

Если я затем использую запрос, предложенный @Gordon Linoff ниже, то есть:

SELECT YEAR(m.dte) as vacyear, MONTH(m.dte) as vacmonth,  
       COUNT(vs.vacancy_id) as totalsubscriptions
FROM (SELECT DATE_ADD(NOW(), INTERVAL -12 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -11 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -10 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -9 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -8 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -7 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -6 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -5 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -4 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -3 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -2 MONTH) as dte UNION ALL
      SELECT DATE_ADD(NOW(), INTERVAL -1 MONTH) as dte
     ) as m LEFT JOIN
     vacancies v
     ON v.create_time >= m.dte AND
        v.create_time < m.dte + interval 1 month AND
        v.is_deleted = 0 AND v.org_id = 1 LEFT JOIN
     vacancy_subscriptions vs 
     ON v.vacancy_id = vs.vacancy_id 
WHERE m.dte >= DATE_ADD(NOW(), INTERVAL -12 MONTH)
GROUP BY vacyear, vacmonth
ORDER BY vacyear ASC, vacmonth ASC;

Я получаю следующий результат (сумма намного ниже, чем должна быть): Изображение 174551

Теги:

1 ответ

1

Самое безопасное решение - иметь таблицу календаря или использовать производную таблицу с месяцами, которые вы хотите. Тогда это становится первой таблицей в LEFT JOIN а условия WHERE переходят в предложение ON.

Однако во многих случаях WHERE фильтрует месяцы, которые в противном случае были бы в наборе результатов. Если это имеет место для ваших данных, тогда у вас есть более простое решение. Просто переместите условия WHERE в SELECT:

SELECT YEAR(v.create_time) as vacyear, MONTH(v.create_time) as vacmonth,  
       SUM(v.is_deleted = 0 AND v.org_id = 1) as totalsubscriptions
FROM vacancies v LEFT JOIN
     vacancy_subscriptions vs 
      ON v.vacancy_id = vs.vacancy_id 
WHERE v.create_time >= DATE_ADD(NOW(), INTERVAL -12 MONTH)
GROUP BY vacyear, vacmonth
ORDER BY vacyear ASC, vacmonth ASC;

Я должен подчеркнуть, что это не всегда работает - это требует, чтобы каждый месяц интереса имел некоторую запись в вашей таблице. Если это действительно работает, это, как правило, самое простое решение.

РЕДАКТИРОВАТЬ:

В противном случае вам нужно решение, которое является некоторым вариантом:

SELECT YEAR(m.dte) as vacyear, MONTH(m.dte) as vacmonth,  
       COUNT(vs.vacancy_id) as totalsubscriptions
FROM (SELECT date('2017-05-01') as dte UNION ALL
      SELECT date('2017-06-01') as dte UNION ALL
      . . .
      SELECT date('2018-04-01') as dte
     ) m LEFT JOIN
     vacancies v
     ON v.create_time >= m.dte AND
        v.create_time < m.dte + interval 1 month AND
        v.is_deleted = 0 AND v.org_id = 1 LEFT JOIN
     vacancy_subscriptions vs 
     ON v.vacancy_id = vs.vacancy_id 
WHERE m.dte >= DATE_ADD(NOW(), INTERVAL -12 MONTH)
GROUP BY vacyear, vacmonth
ORDER BY vacyear ASC, vacmonth ASC;

"В некотором варианте" учитывается, что у вас может быть другой метод расчета списка месяцев.

  • 0
    Таблица с фиксированным календарем не является самым безопасным вариантом в многопользовательской среде. Что если другому администратору также необходимо использовать таблицу календаря, и то, и другое одновременно портит другие результаты, если вы хотите использовать календарь? Таблица использует временный стол, который виден / может использоваться только тем, кто его создал.
  • 0
    @RaymondNijland. , , Как два пользователя, читающие таблицу, "испортят друг друга"?
Показать ещё 9 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню