简体   繁体   中英

SQL query with Count(*) and select missing month also

I'm using a MySQL database.

I want to count all row in a month from a table. This script produces for me a correct output:

SELECT Count(*) as size, DATE_FORMAT(mydate, "%Y-%m") as mydate
FROM Table
GROUP BY DATE_FORMAT(mydate, "%Y-%m") ASC

The output is a table with a number and a date attribute. For example:

10 | 2011-01
40 | 2011-03
20 | 2011-05

My problem is that the month which hasn't any row in my database won't appear at the output. Which is normal at now.

I would like to write an sql query which produces an output like this:

10 | 2011-01
0  | 2011-02
40 | 2011-03
0  | 2011-04
20 | 2011-05

I would like to use just the pure SQL language for this problem if it is possible.

Could anyone help me with this SQL script?

I'd recommend creating a permanent dates table and left joining to that (its pretty handy), or you could make a temp one filled with dates:

CREATE TEMPORARY TABLE daterange (dte DATE); 

SET @counter := -1;
WHILE (@counter < DATEDIFF(DATE(_todate), DATE(_fromdate))) DO 
    INSERT daterange VALUES (DATE_ADD(_fromdate, INTERVAL @counter:=@counter + 1 DAY));
END WHILE;

and then using your query as a subquery, do a left join to get the missing dates

SELECT ifnull(data.thecount,0) as theCount, daterange.dte FROM 
daterange LEFT JOIN
(
   SELECT COUNT(*) AS theCount, DATE_FORMAT(dates.dte, "%Y-%m") as mydate
   FROM table
   GROUP BY DATE_FORMAT(dates.dte, "%Y-%m") ASC
) DATA 
    ON daterange.dte = DATA.mydate
   ORDER BY daterange.dte asc

drop your temp table when finished:

DROP TEMPORARY TABLE daterange

EDIT: The left join was very very wrong, fixed here. You may need to play around with the formatting

See my fiddle .

Solution is based on calendar tables article .

Full listing is below:

/*Build a calendar*/
CREATE TABLE `Int` ( i tinyint );
CREATE TABLE Calendar (dt DATE NOT NULL PRIMARY KEY);
CREATE TABLE Report(
  size INT NOT NULL,
  dt CHAR(7) NOT NULL
);

INSERT INTO `Int` VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

INSERT INTO Calendar (dt)
  SELECT DATE('2010-01-01') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
  FROM `Int` a 
   JOIN `Int` b 
   JOIN `Int` c 
   JOIN `Int` d 
   JOIN `Int` e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) <= 11322
ORDER BY 1;

/*For simplicity I work with your report as ready-to-use table*/

/*10 | 2011-01
40 | 2011-03
20 | 2011-05*/
INSERT INTO Report VALUES
(10, '2011-01'),
(40, '2011-03'),
(20, '2011-05');

/*
Select data from your report.
Also select dummy data with zero sizes, to pad the dates.
After union, we leave only data with max size value 
*/
SELECT MAX(a.size) AS size, a.mydate
FROM
(
  (
    SELECT size, dt AS mydate
    FROM Report
  ) UNION ALL
  (
    SELECT DISTINCT 0 AS size, DATE_FORMAT(dt, "%Y-%m") AS mydate
    FROM Calendar
    WHERE dt BETWEEN '2011-01-01' AND '2011-12-01'
  )
) AS a
GROUP BY a.mydate
ORDER BY a.mydate

Results are

size| mydate
------------
10  | 2011-01
0   | 2011-02
40  | 2011-03
0   | 2011-04
20  | 2011-05
0   | 2011-06
0   | 2011-07
0   | 2011-08
0   | 2011-09
0   | 2011-10
0   | 2011-11
0   | 2011-12

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM