对于我的数据库中的特定表的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。
答案 0 :(得分:250)
LIKE
previously accepted answer不正确。对于LIKE
运算符,Full Text Search及其全文索引不,它有自己的运算符,不适用于任意字符串。它基于词典和词干来运行单词。它 支持前缀匹配,但不支持LIKE
运算符:
LIKE
安装附加模块pg_trgm
,该模块为GIN and GiST trigram indexes提供运算符类,以支持所有LIKE
和ILIKE
模式,而不仅仅是左锚定的:
示例索引:
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个字母的字词仍可使用。 The manual:
每个单词被认为有两个前缀空格和一个空格 确定字符串中包含的三元组时的后缀。
搜索少于3个字母的模式? The manual:
对于
LIKE
和正则表达式搜索,请记住a 没有可提取三卦的模式将退化为全索引扫描。
意思是,索引/位图索引扫描仍然有效(预备语句的查询计划不会中断),它只是不会给你带来更好的性能。通常没有大的损失,因为单字母或双字母字符串几乎没有选择性(超过基础表匹配的百分之几)并且索引支持不会提高性能,因为全表扫描更快。
text_pattern_ops
用于前缀匹配对于左锚定模式(没有前导通配符),您可以获得适合btree索引operator class的最佳模式:text_pattern_ops
或varchar_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索引可以完成这项工作。
dba.SE上这些相关答案的更多细节,解释,示例和链接:
答案 1 :(得分:7)
可能快速的锚定模式具有区分大小写,可以使用索引。即,在匹配字符串的开头没有通配符,因此执行程序可以使用索引范围扫描。 (the relevant comment in the docs is here)Lower和ilike也将失去使用索引的能力,除非您专门为此目的创建索引(参见functional indexes)。
如果您想在字段中间搜索字符串,则应该查看full text或trigram indexes。第一个是Postgres核心,另一个是contrib模块。
答案 2 :(得分:4)
你可以在PostgreSQL中安装Wildspeed,一种不同类型的索引。 Wildspeed确实可以使用%word%通配符,没问题。缺点是指数的大小,这可能很大,非常大。
答案 3 :(得分:2)
请执行下面提到的查询以改进postgresql中的LIKE查询性能。 为更大的表创建这样的索引:
CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)
答案 4 :(得分:1)
我最近在一个包含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
答案 5 :(得分:0)
您的查询可能无法使用您创建的索引,因为:
1)您的LIKE标准以通配符开头。
2)您使用了LIKE标准的函数。
答案 6 :(得分:0)
对于它的价值, Django ,ORM倾向于对所有UPPER(text)
查询使用LIKE
使其不区分大小写,
在UPPER(column::text)
上添加索引大大加快了我的系统速度,
对于前导%,是的,将不使用索引。请参阅此博客以获取详细说明:
https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning
答案 7 :(得分:0)
无论何时在具有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);
类似地,您可以将函数的上半部更改为所需的任何其他函数。