Mysql:按小时分组,无数据则为0
Mysql: Group by Hour, 0 if no data
我有以下查询:
SELECT count(*) as 'totalCalls', HOUR(`end`) as 'Hour'
FROM callsDataTable
WHERE company IN (
SELECT number
FROM products
WHERE products.id IN (@_PRODUCTS))
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
group by HOUR(`end`)
以上查询 returns 只查询了拨打电话的时间:
totalCalls Hour
2 0
1 2
4 7
98 8
325 9
629 10
824 13
665 15
678 16
665 17
606 18
89 22
5 23
所需的输出应该是所有时间,没有调用的地方应该是那个小时的 0 个调用,如下所示:
totalCalls Hour
0 0
0 1
1 2
0 3
0 4
0 5
0 6
4 7
98 8
325 9
629 10
0 11
0 12
824 13
0 14
665 15
678 16
665 17
606 18
0 19
0 20
0 21
89 22
5 23
你需要 Hour
table 然后用 Hour_table
做 left Outer Join
。
这将确保返回所有 hours
。如果 hour
不存在于 callsDataTable
中,则计数将为 0
.
小时 Table
create table hours_table (hours int);
insert into hours_table values(0);
insert into hours_table values(1);
...
insert into hours_table values(23);
查询:
SELECT count(HOUR(`end`)) as 'totalCalls', HT.Hours as 'Hour'
FROM Hours_table HT left Outer join callsDataTable CD
on HT.Hours = HOUR(`end`)
WHERE company IN (
SELECT number
FROM products
WHERE products.id IN (@_PRODUCTS))
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
group by HT.Hours
在 MySQL.
中,从无到有生成价值不是一件容易的事(通常甚至不可能)
我建议一个更简单的方法:
- 在客户端代码中生成一个包含
24
个条目(totalCalls
、Hour
)的列表,其中 0
为 totalCalls
,小时数(来自 0
到 23
) 作为 Hour
。这在任何编程语言中都是一项简单的任务。
- 运行 您已有的查询,获取它的值 returns 并使用它们覆盖上一步生成的空值。
- 尽情享受吧。
首先,您的查询可以用更简单的方式表达为:
SELECT COUNT(*) AS totalCalls, HOUR(`end`) AS `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY HOUR(`end`) AS `Hour`
ORDER BY `Hour` ASC
使用@NoDisplayName 在 中建议的想法:
CREATE TABLE hours_table (hours INT);
INSERT INTO hours_table VALUES(0), (1), (2),
/* put the missing values here */ (23);
您可以加入包含时间的table以获得您想要的结果:
SELECT COUNT(*) AS totalCalls, h.hours AS `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
RIGHT JOIN hours_table h ON h.hours = HOUR(c.`end`)
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY h.hours
ORDER BY h.hours ASC
如果它 运行 太慢(而且我确定它非常慢),您应该研究一种使用类似 end BETWEEN '2015-01-01 00:00:00' AND '2015-01-31 23:59:59'
的方法,而不是比较 YEAR(end)
和 MONTH(end)
。
可以这样实现:
SET @start = STR_TO_DATE(CONCAT(@_YEAR, '-', @_MONTH, '-01 00:00:00'), '%Y-%m-%d %H:%i:%s');
SET @end = DATE_SUB(DATE_ADD(@start, INTERVAL 1 MONTH), INTERVAL 1 SECOND);
SELECT ...
...
AND `end` BETWEEN @start AND @end
...
但这种变化本身并没有帮助。它需要字段 end
上的索引来带来所需的速度改进:
ALTER TABLE callsDataTable ADD INDEX(end);
在连接条件中使用 HOUR(c.end)
是 运行 缓慢的另一个原因。
可以通过将 table hours_table
与第一个查询(的简化版本)生成的结果集相结合来改进:
SELECT IFNULL(totalCalls, 0) AS totalCalls, h.hours AS `Hour`
FROM hours_table h
LEFT JOIN (
SELECT COUNT(*) AS totalCalls, HOUR(`end`) as `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY HOUR(`end`) AS `Hour`
) d ON h.hours = d.`Hour`
ORDER BY h.hours ASC
我有以下查询:
SELECT count(*) as 'totalCalls', HOUR(`end`) as 'Hour'
FROM callsDataTable
WHERE company IN (
SELECT number
FROM products
WHERE products.id IN (@_PRODUCTS))
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
group by HOUR(`end`)
以上查询 returns 只查询了拨打电话的时间:
totalCalls Hour
2 0
1 2
4 7
98 8
325 9
629 10
824 13
665 15
678 16
665 17
606 18
89 22
5 23
所需的输出应该是所有时间,没有调用的地方应该是那个小时的 0 个调用,如下所示:
totalCalls Hour
0 0
0 1
1 2
0 3
0 4
0 5
0 6
4 7
98 8
325 9
629 10
0 11
0 12
824 13
0 14
665 15
678 16
665 17
606 18
0 19
0 20
0 21
89 22
5 23
你需要 Hour
table 然后用 Hour_table
做 left Outer Join
。
这将确保返回所有 hours
。如果 hour
不存在于 callsDataTable
中,则计数将为 0
.
小时 Table
create table hours_table (hours int);
insert into hours_table values(0);
insert into hours_table values(1);
...
insert into hours_table values(23);
查询:
SELECT count(HOUR(`end`)) as 'totalCalls', HT.Hours as 'Hour'
FROM Hours_table HT left Outer join callsDataTable CD
on HT.Hours = HOUR(`end`)
WHERE company IN (
SELECT number
FROM products
WHERE products.id IN (@_PRODUCTS))
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
group by HT.Hours
在 MySQL.
中,从无到有生成价值不是一件容易的事(通常甚至不可能)我建议一个更简单的方法:
- 在客户端代码中生成一个包含
24
个条目(totalCalls
、Hour
)的列表,其中0
为totalCalls
,小时数(来自0
到23
) 作为Hour
。这在任何编程语言中都是一项简单的任务。 - 运行 您已有的查询,获取它的值 returns 并使用它们覆盖上一步生成的空值。
- 尽情享受吧。
首先,您的查询可以用更简单的方式表达为:
SELECT COUNT(*) AS totalCalls, HOUR(`end`) AS `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY HOUR(`end`) AS `Hour`
ORDER BY `Hour` ASC
使用@NoDisplayName 在
CREATE TABLE hours_table (hours INT);
INSERT INTO hours_table VALUES(0), (1), (2),
/* put the missing values here */ (23);
您可以加入包含时间的table以获得您想要的结果:
SELECT COUNT(*) AS totalCalls, h.hours AS `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
RIGHT JOIN hours_table h ON h.hours = HOUR(c.`end`)
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY h.hours
ORDER BY h.hours ASC
如果它 运行 太慢(而且我确定它非常慢),您应该研究一种使用类似 end BETWEEN '2015-01-01 00:00:00' AND '2015-01-31 23:59:59'
的方法,而不是比较 YEAR(end)
和 MONTH(end)
。
可以这样实现:
SET @start = STR_TO_DATE(CONCAT(@_YEAR, '-', @_MONTH, '-01 00:00:00'), '%Y-%m-%d %H:%i:%s');
SET @end = DATE_SUB(DATE_ADD(@start, INTERVAL 1 MONTH), INTERVAL 1 SECOND);
SELECT ...
...
AND `end` BETWEEN @start AND @end
...
但这种变化本身并没有帮助。它需要字段 end
上的索引来带来所需的速度改进:
ALTER TABLE callsDataTable ADD INDEX(end);
在连接条件中使用 HOUR(c.end)
是 运行 缓慢的另一个原因。
可以通过将 table hours_table
与第一个查询(的简化版本)生成的结果集相结合来改进:
SELECT IFNULL(totalCalls, 0) AS totalCalls, h.hours AS `Hour`
FROM hours_table h
LEFT JOIN (
SELECT COUNT(*) AS totalCalls, HOUR(`end`) as `Hour`
FROM callsDataTable c
INNER JOIN products p ON c.company = p.number
AND p.id IN (@_PRODUCTS)
AND YEAR(`end`) = @_YEAR AND MONTH(`end`) = @_MONTH
GROUP BY HOUR(`end`) AS `Hour`
) d ON h.hours = d.`Hour`
ORDER BY h.hours ASC