【发布时间】:2020-01-28 23:39:20
【问题描述】:
我在 Postgres 11 数据库中有两个表:
client table
--------
client_id integer
client_name character_varying
file table
--------
file_id integer
client_id integer
file_name character_varying
client表不分区,file表按client_id分区(按list分区)。当新客户端插入客户端表时,触发器会为文件表创建一个新分区。 文件表有一个外键约束,在 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,文件表中的所有分区都会被扫描。
那么每个选择都应该使用表分区所在的列吗?数据库不能偏离分区修剪标准吗?
编辑: 添加一些信息:
过去,我大部分时间都在使用 Oracle 数据库。
执行计划会是这样的
- 对具有客户端名称的客户端表进行全表扫描以找出 client_id。
- 对文件表执行“PARTITION LIST”访问,其中 SQL Developer 声明 PARTITION_START = KEY 和 PARTITION_STOP = KEY 以指示在计算执行计划时不知道确切的分区,但访问只会对列表进行分区数,根据客户端表中的 client_id 计算得出。
这也是我在 Postgresql 中所期望的。
【问题讨论】:
-
查询计划是在执行查询之前创建的,因此不能基于查询结果。换句话说,在查询运行之前,规划器无法知道
client_name = 'test'表示client_id = 1。 -
感谢您的评论,我添加了一些额外的信息,我希望这些信息来自 Oracle 数据库。
标签: postgresql partitioning database-partitioning pruning