首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Postgres不履约

Postgres不履约
EN

Stack Overflow用户
提问于 2013-07-23 14:44:21
回答 4查看 27.6K关注 0票数 13

有什么办法可以加快查询速度吗?

输入

代码语言:javascript
复制
EXPLAIN SELECT entityid FROM entity e

LEFT JOIN level1entity l1 ON l1.level1id = e.level1_level1id
LEFT JOIN level2entity l2 ON l2.level2id = l1.level2_level2id
WHERE 

l2.userid = 'a987c246-65e5-48f6-9d2d-a7bcb6284c8f' 
AND 
(entityid NOT IN 
(1377776,1377792,1377793,1377794,1377795,1377796... 50000 ids)
)

输出

代码语言:javascript
复制
Nested Loop  (cost=0.00..1452373.79 rows=3865 width=8)
  ->  Nested Loop  (cost=0.00..8.58 rows=1 width=8)
        Join Filter: (l1.level2_level2id = l2.level2id)
        ->  Seq Scan on level2entity l2  (cost=0.00..3.17 rows=1 width=8)
              Filter: ((userid)::text = 'a987c246-65e5-48f6-9d2d-a7bcb6284c8f'::text)
        ->  Seq Scan on level1entity l1  (cost=0.00..4.07 rows=107 width=16)
  ->  Index Scan using fk_fk18edb1cfb2a41235_idx on entity e  (cost=0.00..1452086.09 rows=22329 width=16)
        Index Cond: (level1_level1id = l1.level1id)

好的,这里是一个简化的版本,连接不是瓶颈

代码语言:javascript
复制
SELECT enitityid FROM 
(SELECT enitityid FROM enitity e LIMIT 5000) a

WHERE
(enitityid NOT IN 
(1377776,1377792,1377793,1377794,1377795, ... 50000 ids)
)

问题是找出没有这些ids的权限。

解释

代码语言:javascript
复制
Subquery Scan on a  (cost=0.00..312667.76 rows=1 width=8)
  Filter: (e.entityid <> ALL ('{1377776,1377792,1377793,1377794, ... 50000 ids}'::bigint[]))
  ->  Limit  (cost=0.00..111.51 rows=5000 width=8)
        ->  Seq Scan on entity e  (cost=0.00..29015.26 rows=1301026 width=8)
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-07-24 03:38:59

庞大的IN列表非常低效。理想情况下,PostgreSQL应该识别它并将其转化为它所做的反连接关系,但此时查询规划器不知道如何进行,并且确定这种情况所需的计划时间将花费所有明智地使用NOT IN的查询,因此它必须是一个非常低的成本检查。见this earlier much more detailed answer on the topic

正如大卫·奥尔德里奇()所写的那样,这最好通过将它变成反连接来解决。我会把它写成VALUES列表上的联接,因为PostgreSQL非常快地将VALUES列表解析成关系,但是效果是一样的:

代码语言:javascript
复制
SELECT entityid 
FROM entity e
LEFT JOIN level1entity l1 ON l.level1id = e.level1_level1id
LEFT JOIN level2entity l2 ON l2.level2id = l1.level2_level2id
LEFT OUTER JOIN (
    VALUES
    (1377776),(1377792),(1377793),(1377794),(1377795),(1377796)
) ex(ex_entityid) ON (entityid = ex_entityid)
WHERE l2.userid = 'a987c246-65e5-48f6-9d2d-a7bcb6284c8f' 
AND ex_entityid IS NULL; 

对于足够大的一组值,您甚至可能更好地创建一个临时表,将值放入其中,在其上创建一个COPY,并加入其中。

这里探讨了更多的可能性:

https://stackoverflow.com/a/17038097/398670

票数 36
EN

Stack Overflow用户

发布于 2013-07-23 15:50:48

如果可以重写查询以使用散列反连接,可能会得到更好的结果。

类似于:

代码语言:javascript
复制
with exclude_list as (
  select unnest(string_to_array('1377776,1377792,1377793,1377794,1377795, ...',','))::integer entity_id)
select entity_id
from   entity left join exclude_list on entity.entity_id = exclude_list.entity_id
where  exclude_list.entity_id is null;
票数 7
EN

Stack Overflow用户

发布于 2013-07-23 15:20:53

好吧,我的解决办法是

  • 选择所有实体
  • 左加入实体is上有一个is的所有实体(没有not is是更快的)。
  • 选择联接select为NULL的所有行

如上文所述

http://blog.hagander.net/archives/66-Speeding-up-NOT-IN.html

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17813492

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档