MySQL: filling empty fields with zeroes when using GROUP BY
I've got MySQL table
CREATE TABLE cms_webstat (
ID int NOT NULL auto_increment PRIMARY KEY,
TIMESTAMP_X timestamp DEFAULT CURRENT_TIM开发者_JAVA百科ESTAMP,
# ... some other fields ...
)
which contains statistics about site visitors.
For getting visits per hour I useSELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
which gives me
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 20 | 3 |
| 18 | 2 |
| 15 | 1 |
| 12 | 3 |
| 9 | 1 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
And I'd like to get following:
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 21 | 0 |
| 20 | 3 |
| 19 | 0 |
| 18 | 2 |
| 17 | 0 |
| 16 | 0 |
| 15 | 1 |
| 14 | 0 |
| 13 | 0 |
| 12 | 3 |
| 11 | 0 |
| 10 | 0 |
| 9 | 1 |
| 8 | 0 |
| 7 | 0 |
| 6 | 0 |
| 5 | 0 |
| 4 | 0 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
How should I modify the query to get such result (with one mysql query, without creating temporary tables)?
Is it possible to get such result with one MySQL query?Create another table with a single column,
CREATE TABLE hours_list (
hour int NOT NULL PRIMARY KEY
)
Fill it with all 24 hours.
Then do a join on that table to fill in the zeroes.
SELECT
hs.hour as HOUR, COUNT(ws.ID) AS HOUR_STAT
FROM hours_list hs
LEFT JOIN cms_webstat ws ON hs.hour = hour(ws.TIMESTAMP_X)
GROUP BY hs.hour
ORDER BY hs.hour DESC
This is just the 'why it is not returning` part. Marcus' answer covers the 'how to' part.
The SQL
SELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
gets the count of the records per hour, for the timestamps present in the table
It does not give the details of what is not present in the table. Since there is no recors for the timestamp corresponding to the hour 8
(from your example) the SQL does not return any records.
I've finaly found the answer. Maybe I'm insane, but this works.
SELECT HOUR, max(HOUR_STAT) as HOUR_STAT FROM ( ( SELECT HOUR(TIMESTAMP_X) as HOUR, count(*) as HOUR_STAT FROM cms_webstat WHERE date(TIMESTAMP_X) = date(now()) ) UNION (SELECT 0 as HOUR, 0) UNION (SELECT 1 as HOUR, 0) UNION (SELECT 2 as HOUR, 0) UNION (SELECT 3 as HOUR, 0) UNION (SELECT 4 as HOUR, 0) UNION (SELECT 5 as HOUR, 0) UNION (SELECT 6 as HOUR, 0) UNION (SELECT 7 as HOUR, 0) UNION (SELECT 8 as HOUR, 0) UNION (SELECT 9 as HOUR, 0) UNION (SELECT 10 as HOUR, 0) UNION (SELECT 11 as HOUR, 0) UNION (SELECT 12 as HOUR, 0) UNION (SELECT 13 as HOUR, 0) UNION (SELECT 14 as HOUR, 0) UNION (SELECT 15 as HOUR, 0) UNION (SELECT 16 as HOUR, 0) UNION (SELECT 17 as HOUR, 0) UNION (SELECT 18 as HOUR, 0) UNION (SELECT 19 as HOUR, 0) UNION (SELECT 20 as HOUR, 0) UNION (SELECT 21 as HOUR, 0) UNION (SELECT 22 as HOUR, 0) UNION (SELECT 23 as HOUR, 0) ) AS `combined_table` GROUP BY HOUR ORDER BY HOUR DESC
One MySQL query as desired.
$sql = 'SELECT g, MAX(v) AS v, MAX(c) AS c FROM (';
$sql .= '(SELECT DATE_FORMAT(viewed, \'%d.%m.%Y\') AS g, COUNT(1) AS v, 0 AS c FROM '.$this->prefix.'view WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$sql .= ' UNION (SELECT DATE_FORMAT(clicked, \'%d.%m.%Y\') AS g, 0 AS v, COUNT(1) AS c FROM '.$this->prefix.'clicks WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$today = strtotime("00:00:00");
for ($i=$today; $i>=time()-30*86400; $i-=86400) {
$sql .= ' UNION (SELECT \''.date('d.m.Y',$i).'\' AS g, 0 AS v, 0 AS c)';
}
$sql .= ') AS tmp GROUP BY g ORDER BY g DESC';
$chart = DB::getAll($sql);
p($chart);
Thanks! Made it! From 2 tables, clicks and views, joined.. works. ajaxel.com
精彩评论