好,我有一个带有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
和sum
定位值后的标准行为:
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
您可以执行以下操作:
# 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 |
+------------+------------------------------+
您还可以通过以下查询来实现所需的目的,这可能更易于理解:
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
一些解释:
实际上,您的问题鼓励我们生成一组日期,因为我们希望使用一组连续的日期来“左联接”“您的表”,以匹配“您的表”中没有记录的日期。
由于generate_series函数,这在PostgreSQL中非常容易,但是在MySQL中却不那么容易,因为这样的有用函数不存在。这就是为什么我们需要变得聪明。
这两个解决方案都具有相同的逻辑:我的意思是,它们都为连接到另一个表中的每一行都增加一个日期值(每天),我们称之为“源表”。在上面的答案中(不是我的),“源表”由许多联合和交叉联接构成(生成10万行),在我的情况下,“源表”是“ information_schema.columns”,其中已经包含很多行(1800) +)。
在上述情况下,初始日期固定为1970-01-01,然后它将使该日期递增10万次,以便获得一组以1970-01-01开始的100000个日期。
对于我来说,初始日期固定为您的最小范围日期2020-01-01,然后它将对在information_schema.columns中找到的每一行增加该日期,因此大约是1800次。从2020-01-01开始,您将获得大约1800个日期。
最后,您可以将生成的日期集(无论采用哪种方式)加入表中,以便将所需范围内的每一天相加(值)。
希望可以帮助您了解两个查询背后的逻辑;)