gpt4 book ai didi

sql - 从同一个子查询中选择多个聚合结果

转载 作者:行者123 更新时间:2023-11-29 12:42:13 26 4
gpt4 key购买 nike

我发现自己越来越频繁地做这样的统计——对于tbl1中的每条记录,将其作为条件扫描tbl2并在tbl2上进行聚合,返回聚合结果。使用子查询很容易。

但我需要在同一个子查询的一次传递中进行多次聚合,而不是多次扫描。

例如,这里有一些用于分析的示例数据:

SELECT  to_timestamp((random()*999999)) evt_ts, 
evt_id
INTO temp.test_so_44410354
FROM
(
SELECT *, (random()*2000)::int AS idx
FROM generate_series( 1, 1000) AS evt_id
ORDER BY idx
) AS t

evt_ts |evt_id |
--------------------|-------|
1970-01-01 08:13:15 |109 |
1970-01-01 08:17:22 |762 |
1970-01-01 08:19:28 |630 |
1970-01-01 08:29:34 |429 |
1970-01-01 08:48:28 |70 |

要查看事件在给定时间范围内的分布情况,当前方法和查询计划以及期望的输出:

With wcal AS (
SELECT ts, ts::date as dt
FROM generate_series( '1970-01-01 0:0:0', '1970-01-12 0:0:0', INTERVAL '1 day' ) AS ts
)
SELECT ts, dt
,(
SELECT count(evt_ts)
FROM temp.test_so_44410354 as el
WHERE ( el.evt_ts < wcal.ts AND el.evt_ts > wcal.ts - INTERVAL '1 day' )
) as ecnt
,(
SELECT sum(evt_id) -- should be array_agg, sum is locate to the difference in the results.
FROM temp.test_so_44410354 as el
WHERE ( el.evt_ts < wcal.ts AND el.evt_ts > wcal.ts - INTERVAL '1 day' )
) as eids
FROM wcal

QUERY PLAN |
----------------------------------------------------------------------------------------------------|
CTE Scan on wcal (cost=12.50..21314.08 rows=1000 width=28) |
CTE wcal |
-> Function Scan on generate_series ts (cost=0.00..12.50 rows=1000 width=12) |
SubPlan 2 |
-> Aggregate (cost=10.63..10.64 rows=1 width=8) |
-> Bitmap Heap Scan on test_so_44410354 el (cost=4.33..10.62 rows=5 width=8) |
Recheck Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
-> Bitmap Index Scan on test_so_44410354_idx (cost=0.00..4.33 rows=5 width=0) |
Index Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
SubPlan 3 |
-> Aggregate (cost=10.63..10.64 rows=1 width=8) |
...

ts |dt |ecnt |eids |
--------------------|-----------|-----|------|
1970-01-01 00:00:00 |1970-01-01 |0 | |
1970-01-02 00:00:00 |1970-01-02 |68 |36156 |
1970-01-03 00:00:00 |1970-01-03 |85 |42103 |
1970-01-04 00:00:00 |1970-01-04 |94 |47092 |
1970-01-05 00:00:00 |1970-01-05 |74 |36276 |
....

有 2 个子计划,似乎 pg 必须扫描表 evt_log 两次才能进行两次相同的聚合。我想避免这种情况。因为聚合条件完全相同,如果它可以一次进行多次统计聚合,它可能会快得多。日志可能会变得很大,处理需要相当长的时间。

对提议的解决方案 I 的反馈嵌套的 join 没有按预期工作:

With wcal AS (
SELECT ts, ts::date as dt
FROM generate_series( '1970-01-01 0:0:0', '1970-01-12 0:0:0', INTERVAL '1 day' ) AS ts
)
, ag AS (
SELECT
count(evt_ts) AS ecnt
, sum(evt_id) AS eids
FROM temp.test_so_44410354 as el
JOIN wcal ON ( el.evt_ts < wcal.ts AND el.evt_ts > wcal.ts - INTERVAL '1 day' )
)
SELECT ts, dt, ag.ecnt, eids
FROM wcal
JOIN ag ON true

QUERY PLAN |
----------------------------------------------------------------------------------------------------------------------|
Nested Loop (cost=4239.57..4269.59 rows=1000 width=28) |
CTE wcal |
-> Function Scan on generate_series ts (cost=0.00..12.50 rows=1000 width=12) |
CTE ag |
-> Aggregate (cost=4227.06..4227.07 rows=1 width=16) |
-> Nested Loop (cost=0.28..3671.50 rows=111111 width=12) |
-> CTE Scan on wcal wcal_1 (cost=0.00..20.00 rows=1000 width=8) |
-> Index Scan using test_so_44410354_idx on test_so_44410354 el (cost=0.28..2.54 rows=111 width=12) |
Index Cond: ((evt_ts < wcal_1.ts) AND (evt_ts > (wcal_1.ts - '1 day'::interval))) |
-> CTE Scan on ag (cost=0.00..0.02 rows=1 width=16) |
-> CTE Scan on wcal (cost=0.00..20.00 rows=1000 width=12) |

ts |dt |ecnt |eids |
--------------------|-----------|-----|-------|
1970-01-01 00:00:00 |1970-01-01 |920 |464436 |
1970-01-04 00:00:00 |1970-01-04 |920 |464436 |
1970-01-05 00:00:00 |1970-01-05 |920 |464436 |
1970-01-06 00:00:00 |1970-01-06 |920 |464436 |
1970-01-07 00:00:00 |1970-01-07 |920 |464436 |
....

对提议的解决方案 II 的反馈:写法不同,但查询计划与 2 个子计划基本相同。

DROP TYPE temp.my_agg ;
CREATE TYPE temp.my_agg AS ( ecnt int, eids int );
EXPLAIN
With wcal AS (
SELECT ts, ts::date as dt
FROM generate_series( '1970-01-01 0:0:0', '1970-01-12 0:0:0', INTERVAL '1 day' ) AS ts
)
SELECT ts, dt
,(
SELECT (count(evt_ts),sum(evt_id))::temp.my_agg
FROM temp.test_so_44410354 as el
WHERE ( el.evt_ts < wcal.ts AND el.evt_ts > wcal.ts - INTERVAL '1 day' )
).*
FROM wcal

QUERY PLAN |
--------------------------------------------------------------------------------------------------------------------------------------------|
CTE Scan on wcal (cost=12.50..21349.08 rows=1000 width=20) (actual time=0.182..3.518 rows=12 loops=1) |
CTE wcal |
-> Function Scan on generate_series ts (cost=0.00..12.50 rows=1000 width=12) (actual time=0.057..0.083 rows=12 loops=1) |
SubPlan 2 |
-> Aggregate (cost=10.64..10.66 rows=1 width=32) (actual time=0.140..0.140 rows=1 loops=12) |
-> Bitmap Heap Scan on test_so_44410354 el (cost=4.33..10.62 rows=5 width=12) (actual time=0.049..0.083 rows=77 loops=12) |
Recheck Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
Heap Blocks: exact=66 |
-> Bitmap Index Scan on test_so_44410354_idx (cost=0.00..4.33 rows=5 width=0) (actual time=0.037..0.037 rows=77 loops=12) |
Index Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
SubPlan 3 |
...
Planning time: 0.548 ms |
Execution time: 3.748 ms |

对于建议的解决方案 II,如果聚合作为记录返回,查询计划将如下所示:这大约使速度翻了一番 c.f.以前的查询。 (但记录类型元素不是最终结果。)

QUERY PLAN                                                                                                                                  |
--------------------------------------------------------------------------------------------------------------------------------------------|
CTE Scan on wcal (cost=12.50..10690.79 rows=1000 width=44) (actual time=0.147..1.979 rows=12 loops=1) |
CTE wcal |
-> Function Scan on generate_series ts (cost=0.00..12.50 rows=1000 width=12) (actual time=0.057..0.082 rows=12 loops=1) |
SubPlan 2 |
-> Aggregate (cost=10.64..10.66 rows=1 width=32) (actual time=0.151..0.151 rows=1 loops=12) |
-> Bitmap Heap Scan on test_so_44410354 el (cost=4.33..10.62 rows=5 width=12) (actual time=0.054..0.089 rows=77 loops=12) |
Recheck Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
Heap Blocks: exact=66 |
-> Bitmap Index Scan on test_so_44410354_idx (cost=0.00..4.33 rows=5 width=0) (actual time=0.040..0.040 rows=77 loops=12) |
Index Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
Planning time: 0.381 ms |
Execution time: 2.147 ms |

LATERAL 连接解决方​​案按预期工作。谢谢。

EXPLAIN ANALYZE
With wcal AS (
SELECT ts, ts::date as dt
FROM generate_series( '1970-01-01 0:0:0', '1970-01-12 0:0:0', INTERVAL '1 day' ) AS ts
)
SELECT ts, dt, p.*
FROM wcal
CROSS JOIN LATERAL (
SELECT count(evt_ts), sum(evt_id)
FROM temp.test_so_44410354 as el
WHERE ( el.evt_ts < wcal.ts AND el.evt_ts > wcal.ts - INTERVAL '1 day' )
) p

QUERY PLAN |
------------------------------------------------------------------------------------------------------------------------------------------|
Nested Loop (cost=23.15..10705.79 rows=1000 width=28) (actual time=0.130..1.611 rows=12 loops=1) |
CTE wcal |
-> Function Scan on generate_series ts (cost=0.00..12.50 rows=1000 width=12) (actual time=0.054..0.075 rows=12 loops=1) |
-> CTE Scan on wcal (cost=0.00..20.00 rows=1000 width=12) (actual time=0.061..0.099 rows=12 loops=1) |
-> Aggregate (cost=10.64..10.65 rows=1 width=16) (actual time=0.123..0.123 rows=1 loops=12) |
-> Bitmap Heap Scan on test_so_44410354 el (cost=4.33..10.62 rows=5 width=12) (actual time=0.044..0.074 rows=77 loops=12) |
Recheck Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
Heap Blocks: exact=66 |
-> Bitmap Index Scan on test_so_44410354_idx (cost=0.00..4.33 rows=5 width=0) (actual time=0.033..0.033 rows=77 loops=12) |
Index Cond: ((evt_ts < wcal.ts) AND (evt_ts > (wcal.ts - '1 day'::interval))) |
Planning time: 0.399 ms |
Execution time: 1.778 ms |

ts |dt |count |sum |
--------------------|-----------|------|------|
1970-01-01 00:00:00 |1970-01-01 |0 | |
1970-01-02 00:00:00 |1970-01-02 |68 |36156 |
1970-01-03 00:00:00 |1970-01-03 |85 |42103 |
1970-01-04 00:00:00 |1970-01-04 |94 |47092 |
1970-01-05 00:00:00 |1970-01-05 |74 |36276 |
1970-01-06 00:00:00 |1970-01-06 |96 |43797 |

最佳答案

像什么?

With wcal AS (
SELECT ts, ts::date as dt,
extract( week from ts) as wk,
extract( month from ts ) as mo,
extract( quater from ts ) as qtr,
FROM generate_series( t1, t2, tspan )
)
, ag as (
SELECT count(evt_ts) ecnt, array_aggr(evt_id) eids
FROM ev_log as el
JOIN wcal on (el.ts < wcal.ts AND el.ts < wcal.ts - tspan)
)
SELECT ts, dt, wk, mo, qtr, ecnt, eids
FROM wcal
JOIN ag on true

我不确定连接条件 - 您需要将整个查询放入 OP 才能猜测

已更新

根据 Abelisto 通知 - 谢谢

关于sql - 从同一个子查询中选择多个聚合结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44410354/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com