问的问题
测试表:
CREATE TABLE tbl (id int, str text);
INSERT INTO tbl VALUES
  (1, 'a.b.c.d.e')
, (2, 'x1.yy2.zzz3')     -- different number & length of elements for testing
, (3, '')                -- empty string
, (4, NULL);             -- NULL
LATERAL子查询中的递归CTE
SELECT *
FROM   tbl, LATERAL (
   WITH RECURSIVE cte AS (
      SELECT str
      UNION ALL
      SELECT right(str, strpos(str, '.') * -1)  -- trim leading name
      FROM   cte
      WHERE  str LIKE '%.%'  -- stop after last dot removed
      )
   SELECT ARRAY(TABLE cte) AS result
   ) r;
在CROSS JOIN LATERAL(, LATERAL短)是安全的,因为子查询的总结果总是返回一行。你得到 ...
- ... str = ''基表中带有空字符串元素的数组
- ... str IS NULL基表中具有NULL元素的数组
在子查询中包装了廉价的数组构造函数,因此外部查询中没有聚合。
SQL功能的典范,但rCTE开销可能会阻止最佳性能。
琐碎的元素数量
对于元素数量很少的情况,没有子查询的简单方法可能会更快:
SELECT id, array_remove(ARRAY[substring(str, '(?:[^.]+\.){4}[^.]+$')
                            , substring(str, '(?:[^.]+\.){3}[^.]+$')
                            , substring(str, '(?:[^.]+\.){2}[^.]+$')
                            , substring(str,        '[^.]+\.[^.]+$')
                            , substring(str,               '[^.]+$')], NULL)
FROM   tbl;
假设您最多评论了5个元素。您可以轻松扩展更多。
如果给定域的元素较少,则多余的substring()表达式将返回NULL并被删除array_remove()。
实际上,right(str, strpos(str, '.')由于正则表达式函数更昂贵,因此嵌套数次的above()表达式可能会更快(尽管笨拙)。
@Dudu查询的分支
@Dudu的智能查询可以通过以下方式进行改进generate_subscripts():
SELECT id, array_agg(array_to_string(arr[i:], '.')) AS result
FROM  (SELECT id, string_to_array(str,'.') AS arr FROM tbl) t
LEFT   JOIN LATERAL generate_subscripts(arr, 1) i ON true
GROUP  BY id;
还LEFT JOIN LATERAL ... ON true用于保留具有NULL值的可能行。
PL / pgSQL函数
与rCTE类似的逻辑。比您所拥有的更简单,更快捷:
CREATE OR REPLACE FUNCTION string_part_seq(input text, OUT result text[]) AS
$func$
BEGIN
   LOOP
      result := result || input;  -- text[] || text array concatenation
      input  := right(input, strpos(input, '.') * -1);
      EXIT WHEN input = '';
   END LOOP;
END
$func$  LANGUAGE plpgsql IMMUTABLE STRICT;
该OUT参数将在函数末尾自动返回。
不需要初始化result,因为NULL::text[] || text 'a' = '{a}'::text[]。
这仅'a'在正确键入时有效。NULL::text[] || 'a'(字符串文字)会引发错误,因为Postgres选择了array || array运算符。
strpos()返回0如果没有点被发现,所以right()返回一个空字符串和循环结束。
这可能是这里所有解决方案中最快的。
它们都可以在Postgres 9.3+中使用
(短数组切片符号除外arr[3:]。我在小提琴中添加了上限,以使其在pg 9.3:中可以使用arr[3:999]。)
SQL提琴。
优化搜索的不同方法
我与@ jpmc26(和您自己)在一起:最好采用完全不同的方法。我喜欢jpmc26 reverse()和和的组合text_pattern_ops。
对于部分匹配或模糊匹配,三元组索引会更好。但是,由于您只对整个单词感兴趣,因此全文搜索是另一种选择。我希望索引的大小大大减小,从而获得更好的性能。
pg_trgm以及FTS支持不区分大小写的查询,顺便说一句。
主机名(例如q.x.t.com或)t.com(带内嵌点的单词)被标识为“主机”类型,并被视为一个单词。但是FTS中也有前缀匹配(有时似乎被忽略了)。手册:
  此外,*可以附加到词素以指定前缀匹配:
使用@ jpmc26的聪明点子reverse(),我们可以完成此工作:
SELECT *
FROM   tbl
WHERE  to_tsvector('simple', reverse(str))
    @@ to_tsquery ('simple', reverse('c.d.e') || ':*');
-- or with reversed prefix:  reverse('*:c.d.e')
索引支持:
CREATE INDEX tbl_host_idx ON tbl USING GIN (to_tsvector('simple', reverse(str)));
注意'simple'配置:我们不希望将词干或词库与默认'english'配置一起使用。
另外(可能的查询种类更多),我们可以使用Postgres 9.6中文本搜索的新短语搜索功能。发行说明:
  可以使用新的运算符<->和在tsquery输入中指定词组搜索查询。前者表示词素之前和之后的词素必须按该顺序彼此相邻出现。后者意味着它们必须完全分开词素。<N>N
查询:
SELECT *
FROM   tbl
WHERE  to_tsvector     ('simple', replace(str, '.', ' '))
    @@ phraseto_tsquery('simple', 'c d e');
'.'用空格(' ')代替点(),以防止解析器将't.com'分类为主机名,而是将每个单词用作单独的词素。
以及与之匹配的索引:
CREATE INDEX tbl_phrase_idx ON tbl USING GIN (to_tsvector('simple', replace(str, '.', ' ')));