Sql 使用通配符的慢速连接查询

Sql 使用通配符的慢速连接查询,sql,postgresql,join,Sql,Postgresql,Join,我有一个关键字表,其中包含数百万个条目。它通过多对多关系链接到元素表。我想获得与关键字匹配的所有元素ID。我尝试了这个查询,没有问题,它在几毫秒内返回行 SELECT element_id FROM element_keyword JOIN keyword ON keyword.id = element_keyword.keyword_id WHERE keyword.value like 'LOREM'; 执行计划 "Nested Loop (cost=278.50..53665.

我有一个
关键字
表,其中包含数百万个条目。它通过多对多关系链接到
元素
表。我想获得与关键字匹配的所有元素ID。我尝试了这个查询,没有问题,它在几毫秒内返回行

SELECT element_id FROM element_keyword 
JOIN keyword ON keyword.id = element_keyword.keyword_id
WHERE keyword.value like 'LOREM';   
执行计划

"Nested Loop  (cost=278.50..53665.56 rows=65 width=4)"
"  ->  Index Scan using keyword_value_index on keyword  (cost=0.43..8.45 rows=1 width=4)"
"        Index Cond: ((value)::text = 'LOREM'::text)"
"        Filter: ((value)::text ~~ 'LOREM'::text)"
"  ->  Bitmap Heap Scan on element_keyword  (cost=278.07..53510.66 rows=14645 width=8)"
"        Recheck Cond: (keyword_id = keyword.id)"
"        ->  Bitmap Index Scan on element_keyword_keyword_index  (cost=0.00..274.41 rows=14645 width=0)"
"              Index Cond: (keyword_id = keyword.id)"
然而,当我把通配符放在搜索字符串的末尾时,请求变得非常缓慢。(~60000ms)

执行计划:

"Hash Join  (cost=12.20..3733738.08 rows=19502 width=4)"
"  Hash Cond: (element_keyword.keyword_id = keyword.id)"
"  ->  Seq Scan on element_keyword  (cost=0.00..3002628.08 rows=194907408 width=8)"
"  ->  Hash  (cost=8.45..8.45 rows=300 width=4)"
"        ->  Index Scan using keyword_value_index on keyword  (cost=0.43..8.45 rows=300 width=4)"
"              Index Cond: (((value)::text ~>=~ 'LOREM'::text) AND ((value)::text ~<~ 'LOREN'::text))"
"              Filter: ((value)::text ~~ 'LOREM%'::text)"
背后到底发生了什么?我该怎么解决呢

更新

我不确定这是否有帮助,但这里还有一些测试:

select id from keyword where value like 'LOREM%'; 
-> 6 ids retrieved in 17ms 

select * from element_keyword where keyword_id in (1961746,1961710,2724258,2121442,1633163,1026116); 
-> 40 rows retrieved in 17ms

select * from element_keyword where keyword_id in (select id from keyword where value like 'LOREM%');
-> 40 rows in 63221 ms

我认为您需要一个*_pattern_ops索引(请参阅):

原因是:

WHERE keyword.value like 'LOREM'
对于
LIKE
操作符来说是一个毫无意义的用例。没有通配符(或转义字符),这实际上与:

WHERE keyword.value = 'LOREM'
WHERE keyword.value='LOREM'
。。它可以使用索引来表示相等-因此是一个普通B树索引

若您对匹配前导字符(左锚定搜索模式)感到满意,那个么使用操作符类
text\u pattern\u ops
的B树索引将非常适合您。详细信息:

对于任意模式匹配,请使用pg_trgm模块:

也许不是你想要的。它是基于字典和词干分析的,而不是像你的例子所建议的那样基于文本模式

此外,关键字(值、id)(列的顺序是相关的)上的多列索引可以启用仅索引扫描:


基本上,第二个查询正在进行顺序扫描(出于某些原因,我不理解)。这个顺序扫描花了这么长时间

禁用顺序扫描将强制查询使用索引。因此,如果我在查询之前执行这一行,它会变得非常快

set enable_seqscan to off;

您还可以显示此查询的执行计划吗?首先,如果您设计的数据库需要进行通配符搜索,那么您需要的是全文搜索,而不是通配符。通配符通常会妨碍索引的使用,而且性能非常差。@HLGEM嗯,是的,我可能会使用全文搜索,但经过一些测试后,我得到了大致相同的结果,除了现在……表关键字(value,id)上有索引吗?我不知道postgree,但在SQL server中,最好在索引中包含id列。好吧,我同意第一部分的观点,这更能说明区别。我对左锚定搜索模式很满意,我已经有了文本模式索引。我已经更新了我的帖子来展示我是如何创建索引的。我已经有了这个关于值的索引。我相信如果我没有这样做,像
select*from这样的请求,比如'LOREM%'这样的值将不使用索引。不管怎样。
WHERE keyword.value like 'LOREM'
WHERE keyword.value = 'LOREM'
set enable_seqscan to off;