Postgresql喜欢任何与喜欢

时间:2016-09-30 17:43:21

标签: sql postgresql

我已经尝试过彻底解决这个问题了,所以如果你不耐烦,那就跳到最后看看实际问题是什么......

我正致力于调整我们某个数据库中某些搜索功能的实施方式。为此,我将一些通配符功能添加到我们的应用程序API中,该API与Postgresql接口。

我发现的问题是EXPLAIN ANALYZE次对我没有意义,我试图找出可能出错的地方;它似乎不太可能15个查询优于一个优化查询!

表格Words包含两个相关的问题列:idtexttext列上有一个使用text_pattern_ops选项构建的索引。这就是我所看到的:

首先,使用带有LIKE ANY子句的VALUES some references似乎表明在我的情况下是理想的(找到多个单词):

events_prod=# explain analyze select distinct id from words where words.text LIKE ANY (values('test%'));
                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=6716668.40..6727372.85 rows=1070445 width=4) (actual time=103088.381..103091.468 rows=256 loops=1)
   Group Key: words.id
   ->  Nested Loop Semi Join  (cost=0.00..6713992.29 rows=1070445 width=4) (actual time=0.670..103087.904 rows=256 loops=1)
         Join Filter: ((words.text)::text ~~ "*VALUES*".column1)
         Rows Removed by Join Filter: 214089311
         ->  Seq Scan on words  (cost=0.00..3502655.91 rows=214089091 width=21) (actual time=0.017..25232.135 rows=214089567 loops=1)
         ->  Materialize  (cost=0.00..0.02 rows=1 width=32) (actual time=0.000..0.000 rows=1 loops=214089567)
               ->  Values Scan on "*VALUES*"  (cost=0.00..0.01 rows=1 width=32) (actual time=0.006..0.006 rows=1 loops=1)
 Planning time: 0.226 ms
 Execution time: 103106.296 ms
(10 rows)

正如您所看到的,执行时间非常可怕。

使用LIKE ANY(ARRAY[...的第二次尝试产生:

events_prod=# explain analyze select distinct id from words where words.text LIKE ANY(ARRAY['test%']);
                                                     QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=3770401.08..3770615.17 rows=21409 width=4) (actual time=37399.573..37399.704 rows=256 loops=1)
   Group Key: id
   ->  Seq Scan on words  (cost=0.00..3770347.56 rows=21409 width=4) (actual time=0.224..37399.054 rows=256 loops=1)
         Filter: ((text)::text ~~ ANY ('{test%}'::text[]))
         Rows Removed by Filter: 214093922
 Planning time: 0.611 ms
 Execution time: 37399.895 ms
(7 rows)

正如您所看到的,性能得到了显着提升,但仍然远非理想...... 37秒。列表中有一个单词。将最多三个单词移动总共256行会将执行时间更改为超过100秒。

最后一次尝试,为一个单词做一个LIKE:

events_prod=# explain analyze select distinct id from words where words.text LIKE 'test%';
                                                             QUERY PLAN                                                              
-------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=60.14..274.23 rows=21409 width=4) (actual time=1.437..1.576 rows=256 loops=1)
   Group Key: id
   ->  Index Scan using words_special_idx on words  (cost=0.57..6.62 rows=21409 width=4) (actual time=0.048..1.258 rows=256 loops=1)
         Index Cond: (((text)::text ~>=~ 'test'::text) AND ((text)::text ~<~ 'tesu'::text))
         Filter: ((text)::text ~~ 'test%'::text)
 Planning time: 0.826 ms
 Execution time: 1.858 ms
(7 rows)

正如预期的那样,这是最快的,但是1.85毫秒让我想知道VALUESARRAY方法是否还有其他的东西。

问题

在我的研究中我是否有一些更有效的方法可以在Postgresql中做这样的事情?

select distinct id
  from words
  where words.text LIKE ANY(ARRAY['word1%', 'another%', 'third%']);

1 个答案:

答案 0 :(得分:2)

这有点推测。我认为关键是你的模式:

where words.text LIKE 'test%'

请注意,like模式以常量字符串开头。这意味着Postgres可以对索引进行范围扫描,以查找以'test'开头的单词。

当您再引入多个比较时,优化器会感到困惑,不再考虑多个范围扫描。相反,它决定它需要处理所有行。

这可能是这种重写为您提供所需性能的情况:

select id
from words
where words.text LIKE 'word1%'
union
select id
from words
where words.text LIKE 'another%'
union 
select id
from words
where words.text LIKE 'third%';

注意:

  • 由于distinct
  • ,因此不需要union
  • 如果模式以通配符开头,则无论如何都需要完整扫描。
  • 您可能需要考虑表格上的n-gram或全文索引。