我不得不写一个简单的查询,我去寻找以B或A开头的人们的名字:

SELECT s.name 
FROM spelers s 
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1

我想知道是否有一种方法可以改写这一表现。所以我可以避免 or 和 /或 like?

有帮助吗?

解决方案

您的查询几乎是最佳的。语法不会缩短太多,查询不会变得更快:

SELECT name
FROM   spelers
WHERE  name LIKE 'B%' OR name LIKE 'D%'
ORDER  BY 1;

如果你真的想 缩短语法, ,使用正则表达式 分支:

...
WHERE  name ~ '^(B|D).*'

或稍微快一点 角色类:

...
WHERE  name ~ '^[BD].*'

没有索引的快速测试会产生更快的结果 SIMILAR TO 无论哪种情况,我。
有了适当的b-tree索引, LIKE 通过数量级赢得这场比赛。

阅读有关的基础知识 手册中的图案匹配.

卓越性能索引

如果您关心性能,请为更大表格创建这样的索引:

CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);

通过数量级使这种查询更快。特殊考虑的特定于区域的排序顺序。阅读更多有关 手册中的操作员课程. 。如果您使用的是标准“ C”语言环境(大多数人不使用),则可以使用普通索引(带有默认操作员类)。

这样的索引仅适用于左锚定模式(从字符串的开头匹配)。

SIMILAR TO 或带有基本左锚定表达式的正则表达式也可以使用此索引。但 不是 有分支 (B|D) 或角色类 [BD] (至少在我对PostgreSQL 9.0的测试中)。

Trigram匹配或文本搜索使用特殊的杜松子酒或要点索引。

模式匹配操作员的概述

  • LIKE (~~)简单快速但其功能有限。
    ILIKE (~~*)情况不敏感。
    PG_TRGM扩展了两者的索引支持。

  • ~ (正则表达式匹配)功能强大,但更复杂,除了基本表达式外,任何事情都可能更慢。

  • SIMILAR TO 只是 无意义. 。一个奇特的半身 LIKE 和正则表达式。我从不使用它。见下文。

  • % 是“相似性”运算符,由附加模块提供 pg_trgm. 。见下文。

  • @@ 是文本搜索操作员。见下文。

pg_trgm-搭配

以。。。开始 Postgresql 9.1 您可以促进扩展 pg_trgm 为指数支持 任何 LIKE / ILIKE 模式(以及带有简单的REGEXP模式 ~)使用杜松子酒或要点索引。

详细信息,示例和链接:

pg_trgm 也提供 这些操作员:

  • % - “相似性”运营商
  • <% (换向器: %>) - Postgres 9.6或更高版本中的“ Word_simurility”运算符
  • <<% (换向器: %>>) - Postgres 11或以后

文本搜索

是一种与单独的基础架构和索引类型的特殊类型匹配。它使用词典和词干,是在文档中查找单词的绝佳工具,尤其是对于自然语言。

前缀匹配 也得到支持:

短语搜索 由于Postgres 9.6:

考虑一下 在手册中介绍操作员和功能的概述.

模糊字符串匹配的其他工具

附加模块 fuzzyStrMatch 提供更多选择,但性能通常不如上述所有。

特别是,各种实现 levenshtein() 功能可能是工具性的。

为什么是正则表达式(~)总是比 SIMILAR TO?

答案很简单。 SIMILAR TO 表达式在内部重写正则表达式。所以,每个 SIMILAR TO 表达,有 至少 一个更快的正则表达式(这节省了重写表达式的开销)。使用没有性能 SIMILAR TO 曾经.

以及可以完成的简单表达 LIKE (~~)更快 LIKE 反正。

SIMILAR TO 仅在PostgreSQL中得到支持,因为它最终是SQL标准的早期草稿。他们仍然没有摆脱它。但是有计划将其删除并包括RegeXP匹配 - 或者我听说。

EXPLAIN ANALYZE 揭示它。自己尝试任何桌子!

EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';

揭示:

...  
Seq Scan on spelers  (cost= ...  
  Filter: (name ~ '^(?:B.*)$'::text)

SIMILAR TO 已经用正则表达式重写(~).

这种特殊情况的最终表现

EXPLAIN ANALYZE 揭示更多。尝试使用上述索引:

EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;

揭示:

...
 ->  Bitmap Heap Scan on spelers  (cost= ...
       Filter: (name ~ '^B.*'::text)
        ->  Bitmap Index Scan on spelers_name_text_pattern_ops_idx (cost= ...
              Index Cond: ((prod ~>=~ 'B'::text) AND (prod ~<~ 'C'::text))

在内部,有一个不感知的索引(text_pattern_ops 或使用语言环境 C)这些文本模式运算符重写简单的左派表达式: ~>=~, ~<=~, ~>~, ~<~. 。就是这样 ~, ~~ 或者 SIMILAR TO 一样。

索引也是如此 varchar 类型 varchar_pattern_ops 或者 charbpchar_pattern_ops.

因此,应用于原始问题,这是 最快的方法:

SELECT name
FROM   spelers  
WHERE  name ~>=~ 'B' AND name ~<~ 'C'
    OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER  BY 1;

当然,如果您碰巧搜索 相邻的缩写, ,您可以进一步简化:

WHERE  name ~>=~ 'B' AND name ~<~ 'D'   -- strings starting with B or C

明确使用的收益 ~ 或者 ~~ 很小。如果性能不是您的最高要求,则应该坚持使用标准运算符 - 到达问题中已经拥有的东西。

其他提示

如何在表中添加一列。取决于您的实际要求:

person_name_start_with_B_or_D (Boolean)

person_name_start_with_char CHAR(1)

person_name_start_with VARCHAR(30)

PostgreSQL不支持 基础表中的计算列LA SQL Server 但是新列可以通过触发来维护。显然,此新列将被索引。

或者,一个 表达式索引 会给你一样便宜。例如:

CREATE INDEX spelers_name_initial_idx ON spelers (left(name, 1)); 

在其条件下与表达式匹配的查询可以利用此索引。

这样,在创建或修改数据时会进行性能命中,因此可能仅适用于低活动环境(即写入比阅读少得多)。

你可以 尝试

SELECT s.name
FROM   spelers s
WHERE  s.name SIMILAR TO '(B|D)%' 
ORDER  BY s.name

我不知道以上或您的原始表达方式在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

我过去所做的面临类似的性能问题是要递增最后一个字母的ASCII特征,并在介于两者之间。然后,对于类似功能的子集,您将获得最佳性能。当然,它仅在某些情况下起作用,但是对于您要在名称上搜索的超大数据集,它使性能从Abysmal到可接受。

非常古老的问题,但我找到了一个解决这个问题的另一个快速解决方案:

SELECT s.name 
FROM spelers s 
WHERE ascii(s.name) in (ascii('B'),ascii('D'))
ORDER BY 1

由于函数ascii()仅查看字符串的第一个字符。

要检查缩写,我经常使用铸造 "char" (带双引号)。它不是便携式的,但是很快。在内部,它只是detostost and detotost and返回第一个字符,而“ char”比较操作非常快,因为该类型为1字节固定长度:

SELECT s.name 
FROM spelers s 
WHERE s.name::"char" =ANY( ARRAY[ "char" 'B', 'D' ] )
ORDER BY 1

请注意铸造 "char"ascii() @sole021的slute,但它不兼容UTF8(或任何其他编码),仅返回第一个字节,因此仅在比较与普通的旧7位ASCII字符的情况下使用。

尚未提及处理此类情况的两种方法:

  1. 部分(或分区 - 如果手动为全范围创建)索引 - 当仅需要数据子集时(例如,在某些维护期间或临时报告中)最有用:

    CREATE INDEX ON spelers WHERE name LIKE 'B%'
    
  2. 将表本身划分(使用第一个字符作为分区键) - 在PostgreSQL 10+(较少痛苦的分区)和11+(查询执行过程中的分区修剪)中,该技术尤其值得考虑。

此外,如果对表中的数据进行排序,则可以从使用中受益 Brin索引 (在第一个字符上)。

进行单个角色比较的速度可能更快:

SUBSTR(s.name,1,1)='B' OR SUBSTR(s.name,1,1)='D'
许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top