当运行下面的代码时:
drop table if exists demo;
drop table if exists demo_test;
drop table if exists demo_result;
create table demo as select md5(v::text) from generate_series(1, 1000000) v;
create index on demo (md5 text_pattern_ops);
analyze demo;
create table demo_test
as select left(md5(v::text), 5) || '%' as "patt" from generate_series(2000000, 2000010) v;
create table demo_result (row text);
load 'auto_explain';
set auto_explain.log_min_duration to 0;
set auto_explain.log_analyze to true;
set auto_explain.log_nested_statements to true;
do $$
declare
row record;
pattern text;
begin
for row in select patt from demo_test loop
pattern = row.patt; -- <--- CRUCIAL LINE
insert into demo_result select * from demo where md5 like pattern;
end loop;
end$$;
PostgreSQL生成以下查询计划:
2017-10-02 17:03:48 CEST [18038-23] app=psql barczynski@barczynski LOG: duration: 0.021 ms plan:
Query Text: insert into demo_result select * from demo where md5 like pattern
Insert on demo_result (cost=0.42..8.45 rows=100 width=33) (actual time=0.021..0.021 rows=0 loops=1)
-> Index Only Scan using demo_md5_idx on demo (cost=0.42..8.45 rows=100 width=33) (actual time=0.018..0.018 rows=1 loops=1)
Index Cond: ((md5 ~>=~ '791cc'::text) AND (md5 ~<~ '791cd'::text))
Filter: (md5 ~~ '791cc%'::text)
Heap Fetches: 1
但在删除pattern
变量,并在where
条件下内联row.patt
之后:
insert into demo_result select * from demo where md5 like row.patt;
PostgreSQL将参数视为绑定:
2017-10-02 17:03:02 CEST [17901-23] app=psql barczynski@barczynski LOG: duration: 89.636 ms plan:
Query Text: insert into demo_result select * from demo where md5 like row.patt
Insert on demo_result (cost=0.00..20834.00 rows=5000 width=33) (actual time=89.636..89.636 rows=0 loops=1)
-> Seq Scan on demo (cost=0.00..20834.00 rows=5000 width=33) (actual time=47.255..89.628 rows=1 loops=1)
Filter: (md5 ~~ $4)
Rows Removed by Filter: 999999
我知道后一个计划使用顺序扫描,因为PostgreSQL假设绑定参数以通配符开头。
我的问题是,为什么额外的赋值会打开和关闭绑定参数?
1条答案
按热度按时间gopyfrb31#
差异在于优化程序在查看查询时可用的数据。
对于第一个查询,优化程序可以查看绑定参数。因此,它看到没有通配符,并且知道可以使用索引。
第二个查询不知道模式是什么样子的,所以它不能假设索引是好的。
我猜想,如果您有一个带有前导通配符“%791cc”的模式,您将看到两种方法使用的查询计划是相同的,因为seq_scan将用于这两种方法。