我有办法在Postgres中强制执行特定的加入顺序吗?

我有一个看起来像这样的查询。我已经消除了实际查询中的许多内容,但是这种简化说明了问题所在。剩下的内容应该不太神秘:使用角色/任务安全系统,我试图确定给定的用户是否具有执行给定任务的特权。

select task.taskid
from userlogin
join userrole using (userloginid)
join roletask using (roleid)
join task using (taskid)
where loginname='foobar'
and taskfunction='plugh'

但是我意识到该程序已经知道userlogin的值,因此通过跳过对userlogin的查找并仅填写userloginid可以使查询效率更高,如下所示:
select task.taskid
from userrole
join roletask using (roleid)
join task using (taskid)
where userloginid=42
and taskfunction='plugh'

当我这样做时-从查询中删除一个表并改为硬编码从该表中检索的值-解释计划的时间增加了!在原始查询中,Postgres读取userlogin,然后依次是userrole,roletask和task。但是在新查询中,它决定先读取roletask,然后加入到userrole,即使这需要对roletask进行全文件扫描。

完整的解释计划是:

版本1:
Hash Join  (cost=12.79..140.82 rows=1 width=8)
  Hash Cond: (roletask.taskid = task.taskid)
  ->  Nested Loop  (cost=4.51..129.73 rows=748 width=8)
        ->  Nested Loop  (cost=4.51..101.09 rows=12 width=8)
              ->  Index Scan using idx_userlogin_loginname on userlogin  (cost=0.00..8.27 rows=1 width=8)
                    Index Cond: ((loginname)::text = 'foobar'::text)
              ->  Bitmap Heap Scan on userrole  (cost=4.51..92.41 rows=33 width=16)
                    Recheck Cond: (userrole.userloginid = userlogin.userloginid)
                    ->  Bitmap Index Scan on idx_userrole_login  (cost=0.00..4.50 rows=33 width=0)
                          Index Cond: (userrole.userloginid = userlogin.userloginid)
        ->  Index Scan using idx_roletask_role on roletask  (cost=0.00..1.50 rows=71 width=16)
              Index Cond: (roletask.roleid = userrole.roleid)
  ->  Hash  (cost=8.27..8.27 rows=1 width=8)
        ->  Index Scan using idx_task_taskfunction on task  (cost=0.00..8.27 rows=1 width=8)
              Index Cond: ((taskfunction)::text = 'plugh'::text)

版本2:
Hash Join  (cost=96.58..192.82 rows=4 width=8)
  Hash Cond: (roletask.roleid = userrole.roleid)
  ->  Hash Join  (cost=8.28..104.10 rows=9 width=16)
        Hash Cond: (roletask.taskid = task.taskid)
        ->  Seq Scan on roletask  (cost=0.00..78.35 rows=4635 width=16)
        ->  Hash  (cost=8.27..8.27 rows=1 width=8)
              ->  Index Scan using idx_task_taskfunction on task  (cost=0.00..8.27 rows=1 width=8)
                    Index Cond: ((taskfunction)::text = 'plugh'::text)
  ->  Hash  (cost=87.92..87.92 rows=31 width=8)
        ->  Bitmap Heap Scan on userrole  (cost=4.49..87.92 rows=31 width=8)
              Recheck Cond: (userloginid = 42)
              ->  Bitmap Index Scan on idx_userrole_login  (cost=0.00..4.49 rows=31 width=0)
                    Index Cond: (userloginid = 42)

(是的,我知道在两种情况下成本都很低,而且差异看起来并不重要。但这是在我从查询中删除了许多额外的工作以简化我必须发布的内容之后。真正的查询仍然不算离谱,但我对该原理更感兴趣。)

最佳答案

文档中的此页面描述了如何防止PostgreSQL优化器对联接表重新排序,从而允许您自己控制联接的顺序:

http://www.postgresql.org/docs/current/interactive/explicit-joins.html

09-08 08:10