匹配字符串中的任意字母

Match any letter in a string

Table flights:

ID Path
1 NZ:EU
2 JP:CA
SELECT
    path
FROM
    flights
WHERE
    path ILIKE '%' || 'jpca' || '%'

以上查询无效,需要 return 第二行。但是如果我提供例如:

它也适用于:

也接受正则表达式答案。

如果在匹配前处理 path 列会容易得多

匹配单个字符

(更新问题。)

假设:

  • 所有字符都有意义,包括标点符号。
  • 如果在 path 中找到每个字符,则模式匹配。
  • 匹配case-insensitive.

Lower-case 两个操作数并将它们视为 数组.
如果可以有重复的字母,为了效率,去掉它们。

SELECT path
FROM   flights
WHERE  string_to_array(lower(path), null)
    @> string_to_array(lower('JPCA'), null);

或:

...
WHERE  string_to_array(lower(path), null)  @> '{j,p,c,a}';

Returns 路径包含搜索模式中每个字符的所有行。

@> is the array "contains" operator.

如果 table 很大,在表达式上用 GIN 索引 支持它以使其更快(这是这条路线的重点):

CREATE INDEX flights_path_array_lower_gin_idx ON flights
USING gin (string_to_array(lower(path), null));

相关,包含指向更多内容的链接:

如果你不需要索引支持,简单的检查就可以了:

...
WHERE path ~* ALL (string_to_array('JPCA', null))

~* 是 case-insensitive 正则表达式匹配运算符。

相关:

子串匹配

(原题。)

假设:

  • 必须匹配搜索词中的字符序列。
  • 只有 ASCII 字母才有意义
  • 重复字符很重要
SELECT path
FROM   flights
WHERE  lower(regexp_replace(path, '[^a-zA-Z]', '', 'g')) ~ lower('JPCA');

这将删除除 A-Z 和 a-z 之外的所有字符,并在尝试正则表达式匹配之前将结果转换为小写。相关:

  • LOWER LIKE vs iLIKE

如果你的 table 很大并且你需要它很快,创建一个三元组 expression index:

CREATE INDEX flights_path_expr_idx ON flights
USING gin (lower(regexp_replace(path, '[^a-zA-Z]', '', 'g') gin_trgm_ops);

需要安装附加模块 pg_trgm。参见:

  • PostgreSQL LIKE query performance variations

添加一个“生成的列”到您的 table 和一个普通的 B-tree 索引:

ALTER TABLE flights 
  ADD COLUMN path_ascii text GENERATED ALWAYS AS (lower(regexp_replace(path, '[^a-zA-Z]', '', 'g'))) STORED;

CREATE INDEX flights_path_ascii_trgm_idx ON flights USING gin (path_ascii gin_trgm_ops);

然后:

SELECT path FROM flights WHERE path_ascii ~ 'jpca';

参见:

  • Computed / calculated / virtual / derived columns in PostgreSQL

如果您不希望您的字词中的字符完全按照该顺序出现,而只想在某处指定与这些字符中的每一个匹配的搜索,则可以使用 ALL keyword 来匹配一次有多个 ILIKE 个模式:

SELECT path
FROM flights
WHERE path ILIKE ALL( ARRAY['%j%', '%p%', '%c%', '%a%'] );

现在要从单个字符串生成该数组,您可以使用

SELECT *
FROM flights
WHERE path ILIKE ALL (SELECT '%' || regexp_split_to_table('jpca', '') || '%');

(online demo)