我在一个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,将扫描文件表中的所有分区。
那么每个选择操作都应该使用表进行分区的列吗?数据库不能偏离分区修剪标准吗?
编辑: 添加一些信息:
过去,我大部分时间都在使用Oracle数据库。
执行计划可能如下:
- 使用客户端名称对客户端表进行全表扫描,以查找client_id。
- 对“PARTITION LIST”访问文件表,其中SQL Developer指定PARTITION_START = KEY和PARTITION_STOP = KEY,以表示在计算执行计划时不知道确切的分区,但是访问仅会在计算出的客户端表中进行。
这也是我在Postgresql中预期的。
client_name = 'test'
意味着client_id = 1
。 - klin