How to display all months in summary query results, even if data for a given month is not available

In the table below:

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)

I have the following summary request:

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');

What are the results:

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

Which works fine, but I would like to include all months for a given interval, even if there is no data in this month. Therefore, in the above example, I would like to have this, as a result:

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

Is this possible with SQL?

0
source share
2 answers

You can use the cross join trick to create a date range, and then left join your table with it and aggregate to get the desired results.

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;

It produces:

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

Demo

+1
source

- 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

Source: https://habr.com/ru/post/1664760/


All Articles