【发布时间】:2020-02-04 09:45:32
【问题描述】:
我有两个简单的表格:
Profile (20M entries)
----------------------------
pId | fail
123 | 0
234 | 2
345 | 0
Work (50M entries)
-----------------
pId
123
234
123
345
123
345
我只想在Profile 表中将fail 标记为1,以获取Work 表中超过阈值pId 的条目。 Profile 表中的 pId 已编入索引,我不想触及 fail 不是 0 的行。
我现在使用的查询是:
UPDATE Profile
SET fail = 1
WHERE pId IN
(
SELECT pId
FROM Work
GROUP BY pId
HAVING COUNT(*) > 2
)
AND Profile.fail = 0;
在pgAdmin中,我得到如下解释计划:
"Update on Profile a (cost=1134492.79..1559750.23 rows=5180 width=1014)"
" -> Hash Join (cost=1134492.79..1559750.23 rows=5180 width=1014)"
" Hash Cond: (a.pId = b.pId)"
" -> Seq Scan on Profile a (cost=0.00..425216.00 rows=15462 width=976)"
" Filter: (fail = 0)"
" -> Hash (cost=1134491.95..1134491.95 rows=67 width=32)"
" -> Subquery Scan on b (cost=1134488.78..1134491.95 rows=67 width=32)"
" -> HashAggregate (cost=1134488.78..1134491.28 rows=67 width=4)"
" Group Key: Work.pId"
" Filter: (count(*) > 5)"
" -> Seq Scan on Work (cost=0.00..894341.52 rows=48029452 width=4)"
运行需要几分钟。
现在当这两个表在运行时使用相同的数据创建时,查询计划变为:
"Update on Profile (cost=1250747.42..1251317.47 rows=67 width=386)"
" -> Nested Loop (cost=1250747.42..1251317.47 rows=67 width=386)"
" -> Subquery Scan on "ANY_subquery" (cost=1250746.98..1250750.15 rows=67 width=32)"
" -> HashAggregate (cost=1250746.98..1250749.48 rows=67 width=4)"
" Group Key: Work.pId"
" Filter: (count(*) > 5)"
" -> Seq Scan on Work (cost=0.00..985990.32 rows=52951332 width=4)"
" -> Index Scan using Profile_idx on Profile (cost=0.44..8.46 rows=1 width=348)"
" Index Cond: (pId = "ANY_subquery".pId)"
" Filter: (fail = 0)"
运行需要一个小时。我什至尝试从子查询切换到连接,但它仍然产生相同的结果。任何帮助将不胜感激。
【问题讨论】:
-
首先,如果子查询中有很多结果,
WHERE pId IN (subquery)可能会非常慢。如果结果数量适中(可能不超过一百个左右,但这只是一个模糊的想法),那么使用数组可以大大提高性能:WHERE pId = ANY(ARRAY(subquery))。查询规划器通常会自动执行IN到ARRAY的转换,但有时不会,必须明确完成。 -
@404 感谢您的评论。我在内部查询中返回了大约 20M 行。让我尝试使用
ANY(ARRARY(subquery))并回复您。 -
哎呀!在这种情况下,我不会打扰数组。试试这个,看看性能如何:
UPDATE profile p SET ... WHERE (SELECT COUNT(1) FROM work w WHERE w.pid = p.pid) > 2 AND fail = 0,动态计算每一行的计数可能会更快,希望使用work上的索引,而不是创建一个包含 2000 万行的结果集和然后将每个profile行与这个未索引的结果集进行比较。 -
EXPLAIN (ANALYZE, BUFFERS)这两个查询都会有所帮助。创建表后你运行ANALYZE了吗? -
我确实关联了表格,但它需要更多时间。让我再试一次。 @Laurenz 不,我没有,让我试试。
标签: sql postgresql performance sql-tuning