132

LIKE我已经看到对数据库中特定表的查询的响应时间有很大的变化。有时我会在 200-400 毫秒内得到结果(非常可接受),但有时可能需要 30 秒才能返回结果。

我知道LIKE查询非常耗费资源,但我只是不明白为什么响应时间会有如此大的差异。我已经在该owner1字段上建立了一个 btree 索引,但我认为它对LIKE查询没有帮助。有人有想法么?

示例 SQL:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

我也试过:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

和:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

具有相似的结果。
表行数:约 95,000。

4

8 回答 8

329

FTS 不支持LIKE

先前接受的答案不正确。具有全文索引的全文搜索根本不适用于LIKE运算符,它有自己的运算符并且不适用于任意字符串。它基于字典和词干对单词进行操作。确实支持words 的前缀匹配,但不支持LIKE运算符:

三元组索引LIKE

安装为GIN 和 GiST trigram 索引pg_trgm提供运算符类的附加模块,以支持allpatterns,而不仅仅是左锚定的:LIKEILIKE

示例索引:

CREATE INDEX tbl_col_gin_trgm_idx  ON tbl USING gin  (col gin_trgm_ops);

或者:

CREATE INDEX tbl_col_gist_trgm_idx ON tbl USING gist (col gist_trgm_ops);

示例查询:

SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well

八卦?较短的字符串呢?

索引值中少于 3 个字母的单词仍然有效。手册:

在确定字符串中包含的三元组时,每个单词都被认为具有两个前缀和一个空格后缀。

以及少于 3 个字母的搜索模式?手册:

对于LIKE正则表达式搜索,请记住,没有可提取三元组的模式将退化为全索引扫描。

意思是,索引/位图索引扫描仍然有效(准备好的语句的查询计划不会中断),它只是不会给你带来更好的性能。通常没有大的损失,因为 1 或 2 字母字符串几乎没有选择性(超过基础表匹配的百分之几),并且索引支持一开始不会提高性能,因为全表扫描更快。

text_pattern_opsCOLLATE "C"用于前缀匹配

更新

由于 Postgres 9.1,COLLATE "C"更好。看:

原始答案

对于仅左锚定模式(没有前导通配符),您可以通过合适的运算符类为 btree 索引获得最佳值:text_pattern_opsvarchar_pattern_ops. 这两个标准 Postgres 的内置功能,不需要额外的模块。性能相似,但索引小得多。

示例索引:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

示例查询:

SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard

或者,如果您应该使用“C”语言环境(实际上没有语言环境)运行数据库,那么无论如何都会根据字节顺序对所有内容进行排序,并且使用默认运算符类的普通 btree 索引可以完成这项工作。


进一步阅读

于 2012-11-19T11:08:55.103 回答
10

可能快速的模式是具有大小写敏感的锚定模式,例如可以使用索引。即匹配字符串的开头没有通配符,因此执行程序可以使用索引范围扫描。(文档中的相关评论在此处) Lower 和 ilike 也将失去使用索引的能力,除非您为此目的专门创建索引(请参阅功能索引)。

如果要在字段中间搜索字符串,则应查看全文三元组索引。第一个在 Postgres 核心中,另一个在 contrib 模块中可用。

于 2009-10-14T14:43:35.967 回答
4

您可以安装Wildspeed,这是 PostgreSQL 中的一种不同类型的索引。Wildspeed 确实适用于 %word% 通配符,没问题。缺点是索引的大小,这可能很大,非常大。

于 2010-02-25T21:36:31.290 回答
4

当您在具有 LIKE、ILIKE、upper、lower 等功能的列上使用子句时,postgres 不会考虑您的正常索引。它将通过每一行对表进行全面扫描,因此速度会很慢。

正确的方法是根据您的查询创建一个新索引。例如,如果我想匹配不区分大小写的列,而我的列是 varchar。然后你可以这样做。

create index ix_tblname_col_upper on tblname (UPPER(col) varchar_pattern_ops);

同样,如果您的专栏是文本,那么您可以执行以下操作

create index ix_tblname_col_upper on tblname (UPPER(col) text_pattern_ops);

同样,您可以将函数 upper 更改为您想要的任何其他函数。

于 2019-09-18T13:56:39.870 回答
4

我最近遇到了一个包含 200000 条记录的表的类似问题,我需要重复 LIKE 查询。就我而言,正在搜索的字符串是固定的。其他领域各不相同。因为那样,我能够重写:

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

作为

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

当查询快速返回并验证索引正在使用时,我很高兴EXPLAIN ANALYZE

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms
于 2018-11-21T23:10:01.947 回答
3

请执行下面提到的查询以提高 postgresql 中的 LIKE 查询性能。为更大的表创建这样的索引:

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)
于 2017-10-11T11:40:39.807 回答
1

对于它的价值,Django ORM 倾向于UPPER(text)用于所有LIKE查询以使其不区分大小写,

与其他任何事情不同,添加索引UPPER(column::text)大大加快了我的系统速度。

至于领先的百分比,是的,它不会使用索引。请参阅此博客以获得很好的解释:

https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning

于 2018-09-20T14:55:25.063 回答
0

您的类似查询可能无法使用您创建的索引,因为:

1)您的 LIKE 标准以通配符开头。

2)您已经使用了带有 LIKE 标准的函数。

于 2009-10-14T14:45:09.203 回答