即使没有给定月份的数据,如何显示数据透视查询结果中的所有月份

时间:2017-02-25 08:56:22

标签: mysql sql pivot-table

对于下表:

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?

2 个答案:

答案 0 :(得分:1)

规范方法是从月份列表开始并使用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进行排序是不推荐的行为。
  • 如果您添加SUM()条款,则NULL无法返回ELSE

答案 1 :(得分:0)

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

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