包含零的 PostgreSQL GROUP BY
PostgreSQL GROUP BY that includes zeros
我有一个 SQL 查询 (postgresql),看起来像这样:
SELECT
my_timestamp::timestamp::date as the_date,
count(*) as count
FROM my_table
WHERE ...
GROUP BY the_date
ORDER BY the_date
结果是 table 对 YYYY-MM-DD, count
。
现在我被要求用零填写空白日期。所以如果我之前提供
2022-03-15 3
2022-03-17 1
我现在想要 return
2022-03-15 3
2022-03-16 0
2022-03-17 1
现在我可以轻松地在客户端(相对于数据库)执行此操作,并让我的程序根据来自 postgres 的原始列表计算并 return 零扩充列表到其客户端。但如果我可以告诉 postgresql 包含零可能会更好。
我怀疑这根本不容易,因为 postgres 没有明显的方法知道我在做什么。但是为了学习更多关于 postgres 和 SQL 的知识,我想我应该尝试一下。到目前为止,尝试不太有希望...
在我断定我将其留给我的(postgres 客户端)程序是正确的之前有任何指示吗?
更新
这是一个有趣的案例,我对问题的简化导致了对我不起作用的正确答案。对于那些后来者,我认为有必要记录下接下来发生的事情,因为通过构建 SQL 查询需要一些有趣的曲折。
@a_horse_with_no_name 响应了一个查询,如果我简化自己的查询以匹配,我已经验证该查询有效。不幸的是,我的查询有一些我认为不相关的额外包袱,因此在发布原始问题时已被删除。
这是我的真实(原始)查询,保留了所有名称(如果缩短):
-- current query
SELECT
LEAST(time1, time2, time3, time4)::timestamp::date as the_date,
count(*) as count
FROM reading_group_reader rgr
INNER JOIN ( SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
WHERE LEAST(time1, time2, time3, time4) > current_date - 30
GROUP BY the_date
ORDER BY the_date;
如果我直接将其转换为建议的解决方案,但是,reading_group_reader
和临时 table TT
之间的内部连接会导致左连接变为内部连接(我认为)并且日期序列再次降为零。首先,table TT
是一个 table 因为有时它实际上 是 一个子选择。
所以我将查询转换为:
SELECT
g.dt::date as the_date,
count(*) as count
FROM generate_series(date '2022-03-06', date '2022-04-06', interval '1 day') as g(dt)
LEFT JOIN (
SELECT
LEAST(rgr.time1, rgr.time2, rgr.time3, rgr.time4)::timestamp::date as the_date
FROM reading_group_reader rgr
INNER JOIN (
SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
) rgrt
ON rgrt.the_date = g.dt::date
GROUP BY g.dt
ORDER BY the_date;
但是这会在应该为 0 的地方输出 1 而不是 0。
然而,这样做的原因是因为我现在已经选择了每个日期,所以,当然,每个日期都有一个。我需要包含一个附加字段(将为 NULL)并计数。
所以这个查询最终完成了我想要的:
SELECT
g.dt::date as the_date,
count(rgrt.device_id) as count
FROM generate_series(date '2022-03-06', date '2022-04-06', interval '1 day') as g(dt)
LEFT JOIN (
SELECT
LEAST(rgr.time1, rgr.time2, rgr.time3, rgr.time4)::timestamp::date as the_date,
rgr.device_id
FROM reading_group_reader rgr
INNER JOIN (
SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)
) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
) rgrt(the_date)
ON rgrt.the_date = g.dt::date
GROUP BY g.dt
ORDER BY g.dt;
当然,在重新阅读接受的答案时,我最终看到他确实计算了一个不相关的领域,我在前几次阅读中只是错过了.
您需要加入日期列表。这可以例如使用 generate_series()
完成
SELECT g.dt::date as the_date,
count(t.my_timestamp) as count
FROM generate_series(date '2022-03-01',
date '2022-03-31',
interval '1 day') as g(dt)
LEFT JOIN my_table as t
ON t.my_timestamp::date = g.dt::date
AND ... -- the original WHERE clause goes here!
GROUP BY the_date
ORDER BY the_date;
注意原来的WHERE条件需要进入LEFT JOIN
的连接条件。您不能将它们放入 WHERE 子句中,因为那样会将外连接变回内连接(这意味着不会返回丢失的日期)。
我有一个 SQL 查询 (postgresql),看起来像这样:
SELECT
my_timestamp::timestamp::date as the_date,
count(*) as count
FROM my_table
WHERE ...
GROUP BY the_date
ORDER BY the_date
结果是 table 对 YYYY-MM-DD, count
。
现在我被要求用零填写空白日期。所以如果我之前提供
2022-03-15 3
2022-03-17 1
我现在想要 return
2022-03-15 3
2022-03-16 0
2022-03-17 1
现在我可以轻松地在客户端(相对于数据库)执行此操作,并让我的程序根据来自 postgres 的原始列表计算并 return 零扩充列表到其客户端。但如果我可以告诉 postgresql 包含零可能会更好。
我怀疑这根本不容易,因为 postgres 没有明显的方法知道我在做什么。但是为了学习更多关于 postgres 和 SQL 的知识,我想我应该尝试一下。到目前为止,尝试不太有希望...
在我断定我将其留给我的(postgres 客户端)程序是正确的之前有任何指示吗?
更新
这是一个有趣的案例,我对问题的简化导致了对我不起作用的正确答案。对于那些后来者,我认为有必要记录下接下来发生的事情,因为通过构建 SQL 查询需要一些有趣的曲折。
@a_horse_with_no_name 响应了一个查询,如果我简化自己的查询以匹配,我已经验证该查询有效。不幸的是,我的查询有一些我认为不相关的额外包袱,因此在发布原始问题时已被删除。
这是我的真实(原始)查询,保留了所有名称(如果缩短):
-- current query
SELECT
LEAST(time1, time2, time3, time4)::timestamp::date as the_date,
count(*) as count
FROM reading_group_reader rgr
INNER JOIN ( SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
WHERE LEAST(time1, time2, time3, time4) > current_date - 30
GROUP BY the_date
ORDER BY the_date;
如果我直接将其转换为建议的解决方案,但是,reading_group_reader
和临时 table TT
之间的内部连接会导致左连接变为内部连接(我认为)并且日期序列再次降为零。首先,table TT
是一个 table 因为有时它实际上 是 一个子选择。
所以我将查询转换为:
SELECT
g.dt::date as the_date,
count(*) as count
FROM generate_series(date '2022-03-06', date '2022-04-06', interval '1 day') as g(dt)
LEFT JOIN (
SELECT
LEAST(rgr.time1, rgr.time2, rgr.time3, rgr.time4)::timestamp::date as the_date
FROM reading_group_reader rgr
INNER JOIN (
SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
) rgrt
ON rgrt.the_date = g.dt::date
GROUP BY g.dt
ORDER BY the_date;
但是这会在应该为 0 的地方输出 1 而不是 0。
然而,这样做的原因是因为我现在已经选择了每个日期,所以,当然,每个日期都有一个。我需要包含一个附加字段(将为 NULL)并计数。
所以这个查询最终完成了我想要的:
SELECT
g.dt::date as the_date,
count(rgrt.device_id) as count
FROM generate_series(date '2022-03-06', date '2022-04-06', interval '1 day') as g(dt)
LEFT JOIN (
SELECT
LEAST(rgr.time1, rgr.time2, rgr.time3, rgr.time4)::timestamp::date as the_date,
rgr.device_id
FROM reading_group_reader rgr
INNER JOIN (
SELECT group_id, group_type ::group_type_name
FROM (VALUES (31198, 'excerpt')) as T(group_id, group_type)
) TT
ON TT.group_id = rgr.group_id
AND TT.group_type = rgr.group_type
) rgrt(the_date)
ON rgrt.the_date = g.dt::date
GROUP BY g.dt
ORDER BY g.dt;
当然,在重新阅读接受的答案时,我最终看到他确实计算了一个不相关的领域,我在前几次阅读中只是错过了.
您需要加入日期列表。这可以例如使用 generate_series()
SELECT g.dt::date as the_date,
count(t.my_timestamp) as count
FROM generate_series(date '2022-03-01',
date '2022-03-31',
interval '1 day') as g(dt)
LEFT JOIN my_table as t
ON t.my_timestamp::date = g.dt::date
AND ... -- the original WHERE clause goes here!
GROUP BY the_date
ORDER BY the_date;
注意原来的WHERE条件需要进入LEFT JOIN
的连接条件。您不能将它们放入 WHERE 子句中,因为那样会将外连接变回内连接(这意味着不会返回丢失的日期)。