为不存在的数据返回空行
Posted
技术标签:
【中文标题】为不存在的数据返回空行【英文标题】:return empty rows for not existsting data 【发布时间】:2020-01-20 14:55:11 【问题描述】:好的,我有一个包含 date
列和 integer
列的表,我想检索所有行
在特定日期范围内按日期分组;由于不是每天都有行,是否可以让mysql以默认值返回那些天的行?
示例
源表:
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
具有空行的期望行为/结果:
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
【问题讨论】:
我觉得这个问题可以帮你解决你的问题:***.com/questions/2157282/… 【参考方案1】:您可以执行以下操作:
# 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);
这将创建一个基本上所有日期的列表。然后,您可以筛选出您感兴趣的日期,加入您的餐桌和小组。
您还可以将 where 语句中的日期替换为子查询(表的最小和最大日期)以使其动态
这是一种变通方法,但确实有效。
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 |
+------------+------------------------------+
【讨论】:
它可以工作,但它不能很好地扩展,因为我事先不知道间隔,如果存在这样的功能,我正在寻找更程序化/自动的东西 如何确定?可能你可以用一个子查询替换 where 语句的硬编码部分.. 抱歉,我看错了你的代码,它完全符合我的需要,即使我发现它背后的逻辑很难理解,谢谢 @fudoFROM
部分中的大子查询生成从“1970-01-01”开始的每个日期,包括接下来的 100,000 天。【参考方案2】:
您还可以使用以下查询来实现您想要的,这可能更容易理解:
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;
FIND A DEMO HERE
您可以设置一些变量来修复最小和最大日期:
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
一些解释:
事实上,您的问题鼓励我们生成一组日期,因为我们正在寻找具有连续日期集的“左连接”“您的表”,以便匹配“您的表”中没有记录的日期。
由于有 generate_series 函数,这在 PostgreSQL 中会很容易,但在 MySQL 中并不容易,因为不存在这样一个有用的函数。这就是为什么我们需要聪明。
这里的两种解决方案背后都有相同的逻辑:我的意思是它们都为加入另一个表的每一行增加一个日期值(每天),我们称之为“源表”。在上面的答案(不是我的)中,“源表”由许多联合和交叉连接组成(它生成 100k 行),在我的例子中,“源表”是“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 个日期结束。
最后,您可以将生成的日期集(无论以何种方式)加入您的表格,以便在您想要的范围内为每一天求和(值)。
希望这能帮助您理解这两个查询背后的逻辑;)
【讨论】:
是的,这是一种更程序化的方法 你能解释一下你的回答的逻辑吗?我正在尝试重现它以创建单个整数行,其值在0
和 n
之间,但我很挣扎
你到底想达到什么目的?您只想要 sum(value) 介于 0 和 n 之间的日期吗?没看懂
我复制粘贴了你的原始答案,因为它完全符合我的需要,我只是试图理解它的逻辑,制作一个更简单的版本,返回 n
行,递增值从 a
到 @ 987654328@;我设法做到了,但我仍然想知道它是如何工作的,尤其是为什么 information_schema.columns
参与其中
你现在可以在上面找到我的解释了!以上是关于为不存在的数据返回空行的主要内容,如果未能解决你的问题,请参考以下文章
SetUbiquitous 为不存在的文件显示“文件已存在”
为不存在的表 slick scala (Slick 3.0.0, scala) 创建一个类 Table