如何在分隔符之后生成所有尾随子字符串?

Bo *_*nes 8 postgresql pattern-matching array

给定一个可能包含多个分隔符实例的字符串,我想生成从该字符开始的所有子字符串。

例如,给定一个像'a.b.c.d.e'(或 array {a,b,c,d,e},我想)这样的字符串,我想生成一个像这样的数组:

{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
Run Code Online (Sandbox Code Playgroud)

预期用途是作为填充一列的触发器,以便在写入另一列时更容易地查询域名部分(即,查找全部q.x.t.com以进行查询t.com)。

解决这个问题似乎是一种笨拙的方法(而且很可能是),但现在我很好奇如何用(Postgres')SQL 编写这样的函数。

这些是电子邮件域名,因此很难说元素的最大可能数量是多少,但绝大多数肯定会小于 5。

Dav*_*itz 5

我想这是我的最爱。


create table t (id int,str varchar(100));
insert into t (id,str) values (1,'a.b.c.d.e'),(2,'xxx.yyy.zzz');
Run Code Online (Sandbox Code Playgroud)

select      id
           ,array_to_string((string_to_array(str,'.'))[i:],'.')

from        t,unnest(string_to_array(str,'.')) with ordinality u(token,i)
;
Run Code Online (Sandbox Code Playgroud)
+----+-----------------+
| id | array_to_string |
+----+-----------------+
|  1 | a.b.c.d.e       |
|  1 | b.c.d.e         |
|  1 | c.d.e           |
|  1 | d.e             |
|  1 | e               |
|  2 | xxx.yyy.zzz     |
|  2 | yyy.zzz         |
|  2 | zzz             |
+----+-----------------+
Run Code Online (Sandbox Code Playgroud)

阵列

select      id
           ,array_agg(array_to_string((string_to_array(str,'.'))[i:],'.'))

from        t,unnest(string_to_array(str,'.')) with ordinality u(token,i)

group by    id
;
Run Code Online (Sandbox Code Playgroud)
+----+-------------------------------------------+
| id |                 array_agg                 |
+----+-------------------------------------------+
|  1 | {"a.b.c.d.e","b.c.d.e","c.d.e","d.e","e"} |
|  2 | {"xxx.yyy.zzz","yyy.zzz","zzz"}           |
+----+-------------------------------------------+
Run Code Online (Sandbox Code Playgroud)


jpm*_*c26 3

我认为您不需要在这里单独开设专栏;这是一个 XY 问题。您只是想进行后缀搜索。有两种主要方法可以对其进行优化。

将后缀查询变成前缀查询

基本上,你可以通过逆转一切来做到这一点。

首先在列的背面创建一个索引:

CREATE INDEX ON yourtable (reverse(yourcolumn) text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

然后使用相同的查询:

SELECT * FROM yourtable WHERE reverse(yourcolumn) LIKE reverse('%t.com');
Run Code Online (Sandbox Code Playgroud)

UPPER如果你想让它不区分大小写,你可以抛出一个调用:

CREATE INDEX ON yourtable (reverse(UPPER(yourcolumn)) text_pattern_ops);
SELECT * FROM yourtable WHERE reverse(UPPER(yourcolumn)) LIKE reverse(UPPER('%t.com'));
Run Code Online (Sandbox Code Playgroud)

三字索引

另一个选项是三元组索引。LIKE 'something%something'如果您需要中缀查询(或LIKE '%something%'类型查询),您绝对应该使用它。

首先启用 trigram 索引扩展:

CREATE EXTENSION pg_trgm;
Run Code Online (Sandbox Code Playgroud)

(这应该是 PostgreSQL 开箱即用的,无需任何额外安装。)

然后在您的列上创建一个三元组索引:

CREATE INDEX ON yourtable USING GIST(yourcolumn gist_trgm_ops);
Run Code Online (Sandbox Code Playgroud)

然后只需选择:

SELECT * FROM yourtable WHERE yourcolumn LIKE '%t.com';
Run Code Online (Sandbox Code Playgroud)

同样,如果您愿意,您可以添加一个UPPER以使其不区分大小写:

CREATE INDEX ON yourtable USING GIST(UPPER(yourcolumn) gist_trgm_ops);
SELECT * FROM yourtable WHERE UPPER(yourcolumn) LIKE UPPER('%t.com');
Run Code Online (Sandbox Code Playgroud)

你的问题如所写

三元组索引实际上使用您所要求的更通用的形式来工作。它将字符串分解成片段(三元组)并基于这些片段构建索引。然后,索引可用于比顺序扫描更快地搜索匹配项,但适用于中缀以及后缀和前缀查询。尽可能避免重新发明别人开发的东西。

制作人员

这两个解决方案几乎都是来自选择 PostgreSQL 文本搜索方法。我强烈建议您阅读它,以详细分析 PotsgreSQL 中可用的文本搜索选项。