【发布时间】:2014-10-20 15:11:30
【问题描述】:
我们有一个表格,其中包含原始分析数据(如 Google Analytics(分析)和类似数据),用于查看我们视频的观看次数。它包含原始观看次数、下载次数、加载次数等数字。每个视频都由 video_id 标识。
数据是每天记录的,但是因为我们需要每天提取一些指标,所以可以包含特定 video_id 的多条记录。示例:
date | video_id | country | source | downloads | etc...
----------------------------------------------------------------
2014-01-02 | 1 | us | facebook | 10 |
2014-01-02 | 1 | dk | facebook | 13 |
2014-01-02 | 1 | dk | admin | 20 |
我有一个查询,我需要获取在某个日期之后有新数据的所有视频的汇总数据。要获取视频 ID,我执行以下查询:SELECT video_id FROM table WHERE date >= '2014-01-01' GROUP BY photo_id(或者,我可以在没有 GROUP BY 的情况下执行 DISTINCT(video_id),性能相同)。
拥有这些 ID 后,我需要总聚合数据(一直)。结合起来,这变成了以下查询:
SELECT
video_id,
SUM(downloads),
SUM(loads),
<more SUMs),
FROM
table
WHERE
video_id IN (SELECT video_id FROM table WHERE date >= '2014-01-01' GROUP BY video_id)
GROUP BY
video_id
我们对大约 10 列求和(5-10 取决于查询)。 EXPLAIN ANALYZE 给出以下内容:
GroupAggregate (cost=2370840.59..2475948.90 rows=42537 width=72) (actual time=153790.362..162668.962 rows=87661 loops=1)
-> Sort (cost=2370840.59..2378295.16 rows=2981826 width=72) (actual time=153790.329..155833.770 rows=3285001 loops=1)
Sort Key: table.video_id
Sort Method: external merge Disk: 263528kB
-> Hash Join (cost=57066.94..1683266.53 rows=2981826 width=72) (actual time=740.210..143814.921 rows=3285001 loops=1)
Hash Cond: (table.video_id = table.video_id)
-> Seq Scan on table (cost=0.00..1550549.52 rows=5963652 width=72) (actual time=1.768..47613.953 rows=5963652 loops=1)
-> Hash (cost=56924.17..56924.17 rows=11422 width=8) (actual time=734.881..734.881 rows=87661 loops=1)
Buckets: 2048 Batches: 4 (originally 1) Memory Usage: 1025kB
-> HashAggregate (cost=56695.73..56809.95 rows=11422 width=8) (actual time=693.769..715.665 rows=87661 loops=1)
-> Index Only Scan using table_recent_ids on table (cost=0.00..52692.41 rows=1601328 width=8) (actual time=1.279..314.249 rows=1614339 loops=1)
Index Cond: (date >= '2014-01-01'::date)
Heap Fetches: 0
Total runtime: 162693.367 ms
如您所见,它使用(相当大的)外部磁盘合并排序并且需要很长时间。我不确定为什么首先会触发排序,我正在寻找一种方法来避免它或至少将它最小化。我知道增加work_mem 可以缓解外部磁盘合并,但在这种情况下,它似乎过多,并且将 work_mem 设置在 500MB 以上似乎是个坏主意。
该表有两个(相关)索引:一个单独在 video_id 上,另一个在 (date, video_id) 上。
编辑:运行 ANALYZE table 后更新查询。
【问题讨论】:
-
您的执行计划与您的示例查询不匹配。您在那里有一个合并连接,我无法与您向我们展示的 SQL 匹配。你确定你向我们展示了一切吗?
-
完全匹配,我唯一改变的是一些名字。我怀疑合并连接是一种优化,其中内部查询被视为 JOIN 而不是值列表。
-
连接可能是 postgres 将子选择转换为连接的结果。
-
子查询中的
group by没用。摆脱 id,它不是必需的。另外,似乎根本不需要子选择,因为它从与外部查询相同的表中进行选择。这可以用一个简单的where date >= '2014-01-01'条件替换 -
a_horse_with_no_name:不,它不能。我想要在特定日期之后有数据的视频,但我想要这些视频的聚合数据所有时间(不仅仅是在给定日期之后)。因此,它不能合并。
标签: sql postgresql aggregate