此查询的 Postgresql 索引?
Posted
技术标签:
【中文标题】此查询的 Postgresql 索引?【英文标题】:Postgresql index for this query? 【发布时间】:2020-01-18 08:35:55 【问题描述】:是否有任何索引有助于此类查询?
select p.name
from person p
where 'abjohncde' like '%'||p.name||'%'
使用 gin 和 gin_trgm_ops 的索引可用于此查询:
select p.name from person p where p.name like '%john%'
但 gin 不适用于第一个查询。
【问题讨论】:
你需要一个三元索引depesz.com/2011/02/19/waiting-for-9-1-faster-likeilike 【参考方案1】:pg_trgm
适用于select * from table where field like '%pattern%'
之类的情况,但不幸的是,它在select * from table where 'abcde' like '%'||field||'%'
或select * from table where 'abcde' ~ field
之类的情况下无济于事(语义相同,但第一个更快)
相反,您可以尝试手动创建三元组索引:
create or replace function my_trgm(x text)
returns text[]
language sql
immutable strict
as $$
select
case when length(x) > 3 then
(select array_agg(distinct lower(substr(x, i, 3))) from generate_series(1, length(x)-2) as i)
else array[x]
end
$$;
create or replace function public.my_trgm_1(x text)
returns text[]
language sql
immutable strict
as $$
select
(select array_agg(distinct lower(substr(x, i, 3))) from generate_series(1, length(x)-2) as i) ||
(select array_agg(distinct lower(substr(x, i, 2))) from generate_series(1, length(x)-1) as i) ||
(select array_agg(distinct lower(substr(x, i, 1))) from generate_series(1, length(x)) as i)
$$;
并测试:
postgres=# create table t as select ((random()*1000)::int)::text as x from generate_series(1, 1000000);
SELECT 1000000
postgres=# create index idx_t_x on t using gin (my_trgm(x));
CREATE INDEX
postgres=# explain (analyze, verbose) select * from t where '16662' like '%'||x||'%';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..13216.67 rows=5000 width=3) (actual time=0.338..65.335 rows=8971 loops=1)
Output: x
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on public.t (cost=0.00..11716.67 rows=2083 width=3) (actual time=0.106..59.276 rows=2990 loops=3)
Output: x
Filter: ('16662'::text ~~ (('%'::text || t.x) || '%'::text))
Rows Removed by Filter: 330343
Worker 0: actual time=0.089..57.952 rows=3126 loops=1
Worker 1: actual time=0.124..58.830 rows=3308 loops=1
Planning Time: 0.061 ms
Execution Time: 65.803 ms
postgres=# explain (analyze, verbose) select * from t where my_trgm(x) <@ my_trgm_1('16662') and '16662' ~ x;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on public.t (cost=184.40..6916.58 rows=43 width=3) (actual time=4.655..70.851 rows=8971 loops=1)
Output: x
Recheck Cond: (my_trgm(t.x) <@ '166,666,662,16,66,62,1,6,2'::text[])
Filter: ('16662'::text ~ t.x)
Heap Blocks: exact=3876
-> Bitmap Index Scan on idx_t_x (cost=0.00..184.39 rows=8586 width=0) (actual time=3.534..3.534 rows=8971 loops=1)
Index Cond: (my_trgm(t.x) <@ '166,666,662,16,66,62,1,6,2'::text[])
Planning Time: 0.578 ms
Execution Time: 71.228 ms
请注意,它在单列表上执行,因此在此特定测试用例中扫描整个表可能比扫描 GIN 索引更有效。
PS:使用一些 PL 语言,如 plpythonu
或 plperl
可能更有效地插入/更新数据。
【讨论】:
有一个开箱即用的解决方案:postgresql.org/docs/current/pgtrgm.html 和 depesz.com/2011/02/19/waiting-for-9-1-faster-likeilike @a_horse_with_no_name 据我了解,这是field ~~ '%<pattern>%'
的解决方案,但不适用于'<constant>' ~~ '%'||<field>||'%'
这很棒,但不知何故,您的解决方案需要更长的时间,如您的示例以及我的实际用例所示。我会多玩一点,调整一下看看。谢谢你的想法。
@user3281157 如果您按原样使用我的解决方案...不要使用 ~
运算符 - 这样会非常昂贵,然后 ~~
(like
和 ~~*
的同义词是 @ 的同义词987654337@; 我用~
只是因为它更短:)以上是关于此查询的 Postgresql 索引?的主要内容,如果未能解决你的问题,请参考以下文章