return 不存在数据的空行
return empty rows for not existsting data
好的,我有一个包含 date
列和 integer
列的 table,我想检索所有行
在特定日期范围内按日期分组;由于不是每一天都有行,是否可以为这些天设置 mysql return 行的默认值?
例子
来源table:
date value
2020-01-01 1
2020-01-01 2
2020-01-03 2
2020-01-07 3
2020-01-08 4
2020-01-08 1
按日期 group
ing 和 sum
ming 值后的标准行为:
2020-01-01 3
2020-01-03 2
2020-01-07 3
2020-01-08 5
需要 behaviour/result 空行:
2020-01-01 3
2020-01-02 0
2020-01-03 2
2020-01-04 0
2020-01-05 0
2020-01-06 0
2020-01-07 3
2020-01-08 5
您可以执行如下操作:
# table creation:
drop table if exists test_table;
create table test_table (your_date date, your_value int(11));
insert into test_table (your_date, your_value) values ('2020-01-01', 1);
insert into test_table (your_date, your_value) values ('2020-01-01', 2);
insert into test_table (your_date, your_value) values ('2020-01-03', 2);
insert into test_table (your_date, your_value) values ('2020-01-07', 3);
insert into test_table (your_date, your_value) values ('2020-01-08', 4);
insert into test_table (your_date, your_value) values ('2020-01-08', 1);
这将创建一个基本上包含所有日期的列表。然后筛选您感兴趣的日期,加入您的 table 和群组。
您还可以将 where 语句中的日期替换为子查询(table 的最小和最大日期)以使其成为动态的
这是一个变通办法,但它确实有效。
select sbqry.base_date, sum(ifnull(t.your_value, 0))
from (select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) base_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) sbqry
left join test_table t on base_date = t.your_date
where sbqry.base_date between '2020-01-01' and '2020-01-08'
group by sbqry.base_date;
输入:
+------------+------------+
| your_date | your_value |
+------------+------------+
| 2020-01-01 | 1 |
| 2020-01-01 | 2 |
| 2020-01-03 | 2 |
| 2020-01-07 | 3 |
| 2020-01-08 | 4 |
| 2020-01-08 | 1 |
+------------+------------+
输出:
+------------+------------------------------+
| base_date | sum(ifnull(t.your_value, 0)) |
+------------+------------------------------+
| 2020-01-01 | 3 |
| 2020-01-02 | 0 |
| 2020-01-03 | 2 |
| 2020-01-04 | 0 |
| 2020-01-05 | 0 |
| 2020-01-06 | 0 |
| 2020-01-07 | 3 |
| 2020-01-08 | 5 |
+------------+------------------------------+
您也可以通过以下查询实现您想要的,这可能更容易理解:
SELECT
date_table.date,
IFNULL(SUM(value),0) as sum_val
FROM (
SELECT DATE_ADD('2020-01-01', INTERVAL (@i:=@i+1)-1 DAY) AS `date`
FROM information_schema.columns,(SELECT @i:=0) gen_sub
WHERE DATE_ADD('2020-01-01',INTERVAL @i DAY) BETWEEN '2020-01-01' AND '2020-01-08'
) date_table
LEFT JOIN test ON test.date_value = date_table.date
GROUP BY date;
您可以设置一些变量来固定最小和最大日期:
SET @date_min = '2020-01-01';
SET @date_max = '2020-01-08';
SELECT DATE_ADD(@date_min, INTERVAL (@i:=@i+1)-1 DAY) AS `date`
FROM information_schema.columns, (SELECT @i:=0) gen_sub
WHERE DATE_ADD(@date_min, INTERVAL @i DAY) BETWEEN @date_min AND @date_max
一些解释:
事实上,您的问题鼓励我们生成一组日期,因为我们希望 'left join' 'your table' 具有一组连续的日期,以便匹配 [=] 中没有记录的日期38=]。
由于 generate_series 函数,这在 PostgreSQL 中会很容易,但在 MySQL 中就不那么容易了,因为不存在这样一个有用的函数。这就是为什么我们需要聪明。
这里的两种解决方案背后都有相同的逻辑:我的意思是它们都为加入另一行的每一行递增日期值(每天)table,我们称之为 'source table'。在上面的答案(不是我的)中,'source table' 是由许多联合和交叉连接组成的(它生成 100k 行),在我的例子中 'source table' 是 'information_schema.columns' 已经包含很多行(1800+)。
在上述情况下,初始日期固定为 1970-01-01,然后它将将该日期递增 100 000 次,以获得一组从 1970-01-01 开始的 100 000 个日期。
在我的例子中,初始日期固定为您的最小范围日期 2020-01-01,然后它将为 information_schema.columns 中找到的每一行递增此日期,因此大约 1800 次。您将以一组从 2020-01-01 开始的大约 1800 个日期结束。
最后,您可以将 table 与这组生成的日期(无论采用何种方式)相结合,以便在您想要的范围内对每一天求和(值)。
希望这能帮助您理解这两个查询背后的逻辑 ;)
好的,我有一个包含 date
列和 integer
列的 table,我想检索所有行
在特定日期范围内按日期分组;由于不是每一天都有行,是否可以为这些天设置 mysql return 行的默认值?
例子
来源table:
date value
2020-01-01 1
2020-01-01 2
2020-01-03 2
2020-01-07 3
2020-01-08 4
2020-01-08 1
按日期 group
ing 和 sum
ming 值后的标准行为:
2020-01-01 3
2020-01-03 2
2020-01-07 3
2020-01-08 5
需要 behaviour/result 空行:
2020-01-01 3
2020-01-02 0
2020-01-03 2
2020-01-04 0
2020-01-05 0
2020-01-06 0
2020-01-07 3
2020-01-08 5
您可以执行如下操作:
# table creation:
drop table if exists test_table;
create table test_table (your_date date, your_value int(11));
insert into test_table (your_date, your_value) values ('2020-01-01', 1);
insert into test_table (your_date, your_value) values ('2020-01-01', 2);
insert into test_table (your_date, your_value) values ('2020-01-03', 2);
insert into test_table (your_date, your_value) values ('2020-01-07', 3);
insert into test_table (your_date, your_value) values ('2020-01-08', 4);
insert into test_table (your_date, your_value) values ('2020-01-08', 1);
这将创建一个基本上包含所有日期的列表。然后筛选您感兴趣的日期,加入您的 table 和群组。
您还可以将 where 语句中的日期替换为子查询(table 的最小和最大日期)以使其成为动态的
这是一个变通办法,但它确实有效。
select sbqry.base_date, sum(ifnull(t.your_value, 0))
from (select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) base_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) sbqry
left join test_table t on base_date = t.your_date
where sbqry.base_date between '2020-01-01' and '2020-01-08'
group by sbqry.base_date;
输入:
+------------+------------+
| your_date | your_value |
+------------+------------+
| 2020-01-01 | 1 |
| 2020-01-01 | 2 |
| 2020-01-03 | 2 |
| 2020-01-07 | 3 |
| 2020-01-08 | 4 |
| 2020-01-08 | 1 |
+------------+------------+
输出:
+------------+------------------------------+
| base_date | sum(ifnull(t.your_value, 0)) |
+------------+------------------------------+
| 2020-01-01 | 3 |
| 2020-01-02 | 0 |
| 2020-01-03 | 2 |
| 2020-01-04 | 0 |
| 2020-01-05 | 0 |
| 2020-01-06 | 0 |
| 2020-01-07 | 3 |
| 2020-01-08 | 5 |
+------------+------------------------------+
您也可以通过以下查询实现您想要的,这可能更容易理解:
SELECT
date_table.date,
IFNULL(SUM(value),0) as sum_val
FROM (
SELECT DATE_ADD('2020-01-01', INTERVAL (@i:=@i+1)-1 DAY) AS `date`
FROM information_schema.columns,(SELECT @i:=0) gen_sub
WHERE DATE_ADD('2020-01-01',INTERVAL @i DAY) BETWEEN '2020-01-01' AND '2020-01-08'
) date_table
LEFT JOIN test ON test.date_value = date_table.date
GROUP BY date;
您可以设置一些变量来固定最小和最大日期:
SET @date_min = '2020-01-01';
SET @date_max = '2020-01-08';
SELECT DATE_ADD(@date_min, INTERVAL (@i:=@i+1)-1 DAY) AS `date`
FROM information_schema.columns, (SELECT @i:=0) gen_sub
WHERE DATE_ADD(@date_min, INTERVAL @i DAY) BETWEEN @date_min AND @date_max
一些解释:
事实上,您的问题鼓励我们生成一组日期,因为我们希望 'left join' 'your table' 具有一组连续的日期,以便匹配 [=] 中没有记录的日期38=]。
由于 generate_series 函数,这在 PostgreSQL 中会很容易,但在 MySQL 中就不那么容易了,因为不存在这样一个有用的函数。这就是为什么我们需要聪明。
这里的两种解决方案背后都有相同的逻辑:我的意思是它们都为加入另一行的每一行递增日期值(每天)table,我们称之为 'source table'。在上面的答案(不是我的)中,'source table' 是由许多联合和交叉连接组成的(它生成 100k 行),在我的例子中 'source table' 是 'information_schema.columns' 已经包含很多行(1800+)。
在上述情况下,初始日期固定为 1970-01-01,然后它将将该日期递增 100 000 次,以获得一组从 1970-01-01 开始的 100 000 个日期。
在我的例子中,初始日期固定为您的最小范围日期 2020-01-01,然后它将为 information_schema.columns 中找到的每一行递增此日期,因此大约 1800 次。您将以一组从 2020-01-01 开始的大约 1800 个日期结束。
最后,您可以将 table 与这组生成的日期(无论采用何种方式)相结合,以便在您想要的范围内对每一天求和(值)。
希望这能帮助您理解这两个查询背后的逻辑 ;)