Luc*_*man 123 postgresql index regular-expression pattern-matching string-searching
我必须编写一个简单的查询,在其中查找以 B 或 D 开头的人名:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Run Code Online (Sandbox Code Playgroud)
我想知道是否有办法重写它以提高性能。所以我可以避免or和/或like?
Erw*_*ter 196
您的查询几乎是最佳的。语法不会变得更短,查询也不会变得更快:
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
Run Code Online (Sandbox Code Playgroud)
如果您真的想缩短语法,请使用带有分支的正则表达式:
...
WHERE name ~ '^(B|D).*'
Run Code Online (Sandbox Code Playgroud)
或者稍微快一点,使用字符类:
...
WHERE name ~ '^[BD].*'
Run Code Online (Sandbox Code Playgroud)
SIMILAR TO对我来说,没有索引的快速测试比在任何一种情况下都会产生更快的结果。
有了适当的 B 树索引,就LIKE以数量级的优势赢得这场比赛。
如果您关心性能,请为更大的表创建这样的索引以支持左锚定搜索模式(从字符串的开头匹配):
CREATE INDEX spelers_name_special_idx ON spelers (name COLLATE "C");
Run Code Online (Sandbox Code Playgroud)
看:
在使用“C”语言环境(非典型)运行的数据库中,一个普通的 B 树索引可以完成这项工作。
在旧版本中(如果您坚持,现在仍然如此),您可以将特殊运算符类text_pattern_ops用于相同的目的:
CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)
SIMILAR TO或者带有基本左锚表达式的正则表达式也可以使用这个索引。但不适用于分支(B|D)或字符类[BD](至少在我对 PostgreSQL 9.0 的测试中)。
Trigram 匹配或文本搜索使用特殊的 GIN 或 GiST 索引。
LIKE( ~~) 简单快速,但功能有限。
ILIKE( ~~*) 不区分大小写的变体。
pg_trgm 扩展了对两者的索引支持。
~ (regular expression match) 功能强大但更复杂,除了基本表达式之外,可能会很慢。
SIMILAR TO只是毫无意义。LIKE和正则表达式的奇特混血儿。我从不使用它。见下文。
%是“相似性”运算符,由附加模块提供pg_trgm。见下文。
@@是文本搜索运算符。见下文。
从PostgreSQL 9.1开始,您可以促进扩展pg_trgm以使用 GIN 或 GiST 索引为任何 LIKE/ILIKE模式(以及带有 的简单正则表达式模式~)提供索引支持。
详细信息、示例和链接:
pg_trgm还提供了这些运算符:
% - “相似性”运算符<%(commutator: %>) - Postgres 9.6 或更高版本中的“word_similarity”运算符<<%(commutator: %>>) - Postgres 11 或更高版本中的“strict_word_similarity”运算符是一种特殊类型的模式匹配,具有单独的基础结构和索引类型。它使用字典和词干提取,是在文档中查找单词的好工具,尤其是对于自然语言。
还支持前缀匹配:
以及自 Postgres 9.6 以来的短语搜索:
考虑手册中的介绍以及运算符和功能的概述。
附加模块fuzzystrmatch提供了更多选项,但性能通常不如上述所有模块。
特别地,该levenshtein()功能的各种实现可能是有用的。
~) 总是比 快SIMILAR TO?答案很简单。SIMILAR TO表达式在内部被重写为正则表达式。因此,对于每个SIMILAR TO表达式,至少有一个更快的正则表达式(这样可以节省重写表达式的开销)。使用SIMILAR TO ever没有性能提升。
无论如何,可以用LIKE( ~~)完成的简单表达式会更快LIKE。
SIMILAR TO仅在 PostgreSQL 中受支持,因为它最终出现在 SQL 标准的早期草案中。他们还没有摆脱它。但是有计划将其删除并包含正则表达式匹配 - 或者我听说过。
EXPLAIN ANALYZE揭示它。自己尝试任何桌子!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
Run Code Online (Sandbox Code Playgroud)
显示:
...
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
Run Code Online (Sandbox Code Playgroud)
SIMILAR TO已用正则表达式 ( ~)重写。
但EXPLAIN ANALYZE揭示的更多。尝试使用上述索引:
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;
Run Code Online (Sandbox Code Playgroud)
显示:
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
Run Code Online (Sandbox Code Playgroud)
在内部,对于不text_pattern_ops识别语言环境(或使用语言环境C)的索引,使用以下文本模式运算符重写简单的左锚表达式:~>=~, ~<=~, ~>~, ~<~。这是的情况下~,~~或SIMILAR TO相似。
对于varchar带有varchar_pattern_ops或char带有 的类型的索引也是如此bpchar_pattern_ops。
因此,应用于原始问题,这是最快的方法:
SELECT name
FROM spelers
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
Run Code Online (Sandbox Code Playgroud)
当然,如果你碰巧搜索相邻的首字母,你可以进一步简化:
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
Run Code Online (Sandbox Code Playgroud)
简单使用~or的收益~~很小。如果性能不是您的首要要求,您应该坚持使用标准运算符 - 达到您在问题中已有的内容。
one*_*hen 11
如何向表中添加一列。根据您的实际需求:
person_name_start_with_B_or_D (Boolean)
person_name_start_with_char CHAR(1)
person_name_start_with VARCHAR(30)
Run Code Online (Sandbox Code Playgroud)
PostgreSQL 不支持基表中的计算列,但可以通过触发器维护新列。显然,这个新列将被索引。
CREATE INDEX spelers_name_initial_idx ON spelers (left(name, 1));
Run Code Online (Sandbox Code Playgroud)
与其条件中的表达式匹配的查询可以使用此索引。
这样,在创建或修改数据时会影响性能,因此可能仅适用于低活动环境(即写入比读取少得多)。
你可以试试
SELECT s.name
FROM spelers s
WHERE s.name SIMILAR TO '(B|D)%'
ORDER BY s.name
Run Code Online (Sandbox Code Playgroud)
不过,我不知道上述内容或您的原始表达式是否可以在 Postgres 中使用。
如果您创建了建议的索引,您也会有兴趣了解它与其他选项的比较情况。
SELECT name
FROM spelers
WHERE name >= 'B' AND name < 'C'
UNION ALL
SELECT name
FROM spelers
WHERE name >= 'D' AND name < 'E'
ORDER BY name
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
179783 次 |
| 最近记录: |