Postgresql何时使用JOIN列进行分区修剪?

问题描述 投票:0回答:1

我在Postgres 11数据库中有两个表:

client table
--------
client_id    integer
client_name  character_varying

file table
--------
file_id      integer
client_id    integer
file_name    character_varying

客户端表未分区,文件表由client_id分区(按列表分区)。将新客户端插入客户端表后,触发器将为文件表创建新分区。文件表具有外键约束,该外键约束引用client_id上的客户机表。

当我执行此SQL(c.client_id = 1)时,一切似乎都很好:

explain  
select *
from client c
join file f using (client_id)
where c.client_id = 1;

使用分区修剪,仅扫描分区file_p1:

Nested Loop  (cost=0.00..3685.05 rows=100001 width=82)
  ->  Seq Scan on client c  (cost=0.00..1.02 rows=1 width=29)
        Filter: (client_id = 1)
  ->  Append  (cost=0.00..2684.02 rows=100001 width=57)
        ->  Seq Scan on file_p1 f  (cost=0.00..2184.01 rows=100001 width=57)
              Filter: (client_id = 1)

但是当我使用诸如“ where c.client_name ='test'”之类的where子句时,数据库将扫描所有分区,并且无法识别出,client_name“ test”等于client_id 1 ::

explain  
select *
from client c
join file f using (client_id)
where c.client_name = 'test';

执行计划:

Hash Join  (cost=1.04..6507.57 rows=100001 width=82)
  Hash Cond: (f.client_id = c.client_id)
  ->  Append  (cost=0.00..4869.02 rows=200002 width=57)
        ->  Seq Scan on file_p1 f  (cost=0.00..1934.01 rows=100001 width=57)
        ->  Seq Scan on file_p4 f_1  (cost=0.00..1934.00 rows=100000 width=57)
        ->  Seq Scan on file_pdefault f_2  (cost=0.00..1.00 rows=1 width=556)
  ->  Hash  (cost=1.02..1.02 rows=1 width=29)
        ->  Seq Scan on client c  (cost=0.00..1.02 rows=1 width=29)
              Filter: ((name)::text = 'test'::text)

因此,对于此SQL,将扫描文件表中的alle分区。

因此,每个选择都应该使用对表进行分区的列?数据库是否无法偏离分区修剪条件?


编辑:要添加一些信息:

过去,我大多数时候都在使用Oracle数据库。

执行计划会是这样

  1. 使用客户端名称在客户端表上进行全表扫描以找出client_id。
  2. 对文件表进行“ PARTITION LIST”访问,SQL Developer在计算执行计划时指出PARTITION_START = KEY和PARTITION_STOP = KEY表示确切的分区是未知的,但是仅对以下列表进行访问分区,这些分区是根据在客户端表中找到的client_id计算的。

这也是我在Postgresql中所期望的。

postgresql partitioning database-partitioning pruning
1个回答
1
投票

The documentation指出可以对动态分区进行修剪

(...)在实际执行查询计划期间。此处也可以执行分区修剪以使用仅在实际查询执行期间才知道的值来删除分区。这包括来自子查询的值和来自执行时参数的值,例如来自参数化嵌套循环联接的值。

如果我理解正确,它适用于准备好的语句或带有子查询的查询,这些查询或查询将分区键值作为参数。使用explain analyse查看动态修剪(我的样本数据在三个分区中包含一百万行):

explain analyze
select *
from file
where client_id = (
    select client_id
    from client
    where client_name = 'test');

Append  (cost=25.88..22931.88 rows=1000000 width=14) (actual time=0.091..96.139 rows=333333 loops=1)
  InitPlan 1 (returns $0)
    ->  Seq Scan on client  (cost=0.00..25.88 rows=6 width=4) (actual time=0.040..0.042 rows=1 loops=1)
          Filter: (client_name = 'test'::text)
          Rows Removed by Filter: 2
  ->  Seq Scan on file_p1  (cost=0.00..5968.66 rows=333333 width=14) (actual time=0.039..70.026 rows=333333 loops=1)
        Filter: (client_id = $0)
  ->  Seq Scan on file_p2  (cost=0.00..5968.68 rows=333334 width=14) (never executed)
        Filter: (client_id = $0)
  ->  Seq Scan on file_p3  (cost=0.00..5968.66 rows=333333 width=14) (never executed)
        Filter: (client_id = $0)
Planning Time: 0.423 ms
Execution Time: 109.189 ms

注意,对分区p2和p3的扫描为never executed

回答您的确切问题,在问题中描述的具有联接的查询中的分区修剪未在Postgres中实现(是吗?)

© www.soinside.com 2019 - 2024. All rights reserved.