在postgres 10.1服务器中,我有一个很大的表,该表按列表值分区,而视图仅按分区列过滤该表。
使用视图时,计划者没有给我最好的计划,我的意思是仅扫描选定的子表。 相反,它总是扫描父表的所有分区。
我已经通过分区列和约束工具创建了索引。 DDL:
Table "parted_mob_matrix"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | not null | | plain | |
delivery_id | integer | | | | Partition key: LIST (delivery_id)
Partitions: parted_mob_matrix_delivery_0 FOR VALUES IN (0),
parted_mob_matrix_delivery_1 FOR VALUES IN (1),
parted_mob_matrix_delivery_10 FOR VALUES IN (10),
....
parted_mob_matrix_delivery_10 FOR VALUES IN (620),
Table "parted_mob_matrix_delivery_620"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | not null | | plain | |
delivery_id | integer | | | | plain | |
Partition of: parted_mob_matrix FOR VALUES IN (620)
Partition constraint: ((delivery_id IS NOT NULL) AND (delivery_id = ANY (ARRAY[620])))
Indexes:
"parted_mob_matrix_delivery_620_delivery_id_idx" btree (delivery_id)
Check constraints:
"parted_mob_matrix_delivery_620_check_delivery" CHECK (delivery_id = 620)
Mi查看代码:
EXPLAIN SELECT
parted_mob_matrix.*
FROM
parted_mob_matrix
1) where parted_mob_matrix.delivery_id in (620)
2) where parted_mob_matrix.delivery_id in (select 620)
我需要在这里使用简化的2
版本(这是对另一个很小的表的真实查询),但是它的计划却大不相同,甚至更糟。
查询计划1(效果不错):
Append (cost=0.00..78308.11 rows=758031 width=738)
-> Seq Scan on parted_mob_matrix_delivery_620 (cost=0.00..78308.11 rows=758031 width=738)
Filter: (delivery_id = 620)
查询计划2(rowset,慢):
Hash Semi Join (cost=0.01..25077311.20 rows=7539693 width=860)
Hash Cond: (parted_mob_matrix_delivery_0.delivery_id = (620))
-> Append (cost=0.00..24942162.20 rows=211111399 width=859)
-> Seq Scan on parted_mob_matrix_delivery_0 (cost=0.00..10.75 rows=250 width=294)
-> Seq Scan on parted_mob_matrix_delivery_1 (cost=0.00..10.75 rows=250 width=294)
-- All the child tables
-> Seq Scan on parted_mob_matrix_delivery_620 (cost=0.00..77929.09 rows=758031 width=738)
-- All the child tables are scanned
如何在类似1
的查询中使用计划2
?
答案 0 :(得分:3)
您可以在PostgreSQL v10中解决问题,将WHERE条件的输入包装为IMMUTABLE plpgsql函数,该函数返回整数数组。根据定义,当查询使用常量参数(...)“ (https://www.postgresql.org/docs/10/xfunc-volatility.html进行调用时,IMMUTABLE plpgsql函数”(...)允许优化器预先评估该函数。
此解决方案应该有效。
示例:
SELECT
parted_mob_matrix.*
FROM
parted_mob_matrix
WHERE parted_mob_matrix.delivery_id = ANY(get_deliveries('cod_011'))
您可以使用的功能:
CREATE OR REPLACE FUNCTION get_deliveries(
high_level_id TEXT
)
RETURNS INTEGER[]
AS $BODY$
DECLARE
_delivery_ids INTEGER[];
BEGIN
EXECUTE format(
$$
SELECT ARRAY_AGG(delivery_id)
FROM
your_table_with_all_delivery_ids
WHERE
high_level_id = '%1$s'
;
$$, high_level_id
) INTO _delivery_ids;
RETURN _delivery_ids;
END;
$BODY$
LANGUAGE plpgsql IMMUTABLE;
答案 1 :(得分:2)
问题是您使用的是PostgreSQL v10,其中分区修剪仅在计划时间进行。
在您的第一个查询中,条件是一个常量,因此可以进行分区修剪。在第二种情况下,它是一个子查询结果(未展平),因此它将不起作用。
在两个查询上运行EXPLAIN
来查看差异。
您应该使用PostgreSQL v11,在该分区上也可以在查询执行时进行分区修剪。