在mysql查询结果中添加过去一年中的所有月份,即使没有行

问题描述 投票:0回答:2

我正在寻找一种方法来检索过去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 |
+----------------+---------+----------+--------------------+

更新:我创建了一个包含一些数据的SQL小提琴! http://www.sqlfiddle.com/#!9/75db76

mysql sql date
2个回答
1
投票

你的逻辑被打破了。您的日历表需要包括月份和年份 - 以及月份的第一个日期(为方便起见)。

然后你可以这样做:

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个月的数据,包括部分第一个月和最后几个月。

1
投票

在on子句中移动与左连接表相关的where条件

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
© www.soinside.com 2019 - 2024. All rights reserved.