【问题标题】:How to display all months in pivot query's results even if no data for given month [duplicate]即使给定月份没有数据,如何在数据透视查询的结果中显示所有月份[重复]
【发布时间】:2017-07-16 04:33:39
【问题描述】:

对于下表:

CREATE TABLE products
(
date DATE,
productname VARCHAR(80),
quantity INT(5)
);

INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-12-16','toy',5);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-12-18','santa',8);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-12-23','tree',15);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-01','toy',10);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-04','santa',20);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-09','tree',30);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-01','toy',40);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-04','santa',30);
INSERT INTO `products`(`date`, `productname`, `quantity`) VALUES ('2016-10-09','tree',20)

我有以下枢轴查询:

SELECT DATE_FORMAT(`date`, '%Y-%m') As Date,
  IFNULL(Sum(Case when `productname` = 'santa' then `quantity` end),0) As santa, 
  IFNULL(Sum(Case when `productname` = 'toy' then `quantity` end),0) As toy, 
  IFNULL(Sum(Case when `productname` = 'tree' then `quantity` end),0) As tree 
FROM `products`
WHERE `Date` BETWEEN '2016-10' and '2016-12'
GROUP BY DATE_FORMAT(`date`, '%Y-%m');

哪些结果:

+---------+-------+------+------+
| Date    | santa | toy  | tree |
+---------+-------+------+------+
| 2016-10 | 50    | 50   | 50   |
+---------+-------+------+------+
| 2016-12 | 8     | 5    | 15   |
+---------+-------+------+------+

效果很好,但我想包括给定时间间隔的所有月份,即使该月没有数据。所以在上面的例子中,我想要这个,结果是:

+---------+-------+------+------+
| Date    | santa | toy  | tree |
+---------+-------+------+------+
| 2016-10 | 50    | 50   | 50   |
+---------+-------+------+------+
| 2016-11 | 0     | 0    | 0    |
+---------+-------+------+------+
| 2016-12 | 8     | 5    | 15   |
+---------+-------+------+------+

SQL 可以吗?

【问题讨论】:

  • 加入一个包含所有月份的表。或者,如果使用 MariaDB,请使用 sequence" table 即时生成所有月份。

标签: mysql sql pivot-table


【解决方案1】:

您可以使用交叉连接技巧来生成日期范围,然后将您的表与它左连接并聚合以获得所需的结果。

select c.ym, 
    coalesce(santa,0) santa,
    coalesce(toy,0) toy,
    coalesce(tree,0) tree
from (
SELECT DATE_FORMAT(`date`, '%Y-%m') As ym,
  IFNULL(Sum(Case when `productname` = 'santa' then `quantity` end),0) As santa, 
  IFNULL(Sum(Case when `productname` = 'toy' then `quantity` end),0) As toy, 
  IFNULL(Sum(Case when `productname` = 'tree' then `quantity` end),0) As tree 
FROM `products`
GROUP BY DATE_FORMAT(`date`, '%Y-%m')
) t right join (select ym
from (
    select date_format(str_to_date('2016-10-01','%Y-%m-%d') + INTERVAL (a.a + (10 * b.a)) MONTH,'%Y-%m') as ym
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
) a
where ym between '2016-10' and '2016-12'
) c on t.ym = c.ym;

生产:

    Date    santa   toy tree
1   2016-10 50  50  50
2   2016-11 0   0   0
3   2016-12 8   5   15

Demo

【讨论】:

  • @Downvoter - 愿意给出理由吗?
  • Shadow 指出这是重复的,但是,感谢您的回答,它帮助我更好地理解了这个问题。 @GurV
【解决方案2】:

规范的方法是从月份列表开始并使用left join

SELECT DATE_FORMAT(m.month_start, '%Y-%m') as yyyymm,
       Sum(Case when productname = 'santa' then quantity else 0 end) As santa, 
       Sum(Case when productname = 'toy' then quantity else 0 end) As toy, 
       Sum(Case when productname = 'tree' then quantity else 0 end) As tree 
FROM (SELECT DATE('2016-10-01') as month_start UNION ALL
      SELECT DATE('2016-11-01') as month_start UNION ALL
      SELECT DATE('2016-12-01') as month_start
     ) m LEFT JOIN
     products p
     ON p.Date >= m.month_start AND
        p.Date < m.month_start + interval 1 month
GROUP BY DATE_FORMAT(m.month_start, '%Y-%m')
ORDER BY yyyymm;

注意事项:

  • 没有必要使用反引号,除非你真的需要它们。查询中的列名和表名不需要它们。
  • 这会手动生成月份列表。如果您有日历表,还可以更轻松地生成列表。
  • 严格来说,ORDER BY 在 MySQL 中不是必需的,但保证 GROUP BY 进行排序已被弃用。
  • 如果包含ELSE 子句,SUM() 不能返回 NULL

【讨论】:

  • Shadow 指出这是重复的,但是,感谢您的回答,它帮助我更好地理解了这个问题。 - @戈登林诺夫
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-05
相关资源
最近更新 更多