PostgreSQL 组合无重复

Posted

技术标签:

【中文标题】PostgreSQL 组合无重复【英文标题】:PostgreSQL combinations without repetitions 【发布时间】:2012-04-24 02:05:34 【问题描述】:

如何在 postgres 中创建函数,该函数将采用字符串或数组并返回一定长度的所有组合?

例如你有 ABC,你想得到 2 个字符的组合,结果应该是:

AB 交流电 公元前

提前感谢您的帮助。

【问题讨论】:

为什么要在 DBMS 中这样做?你不能在应用程序级别做到这一点吗? 我可以在数独求解器中使用 ;-) 我会在多种编程语言中使用它,并且只在一个数据库 PostgreSQL 中使用它,所以我认为这是将它放在一个地方的最简单方法。这不必加入字符串,我只是给出建议应该如何工作。 【参考方案1】:
set search_path='tmp';

WITH ztab AS (
SELECT idx as idx
, substring ( 'WTF!' FROM idx FOR 1) as str
FROM generate_series(1, char_length( 'WTF!' )) idx
)
SELECT t1.str, t2.str
FROM ztab t1
JOIN ztab t2 ON t2.idx > t1.idx
        ;

结果:

 str | str 
-----+-----
 W   | T
 W   | F
 W   | !
 T   | F
 T   | !
 F   | !
(6 rows)

不幸的是,我找不到避免双字符串常量的方法。 (但整个事情可以打包成一个函数)如果没有重复的字符(或者你想抑制它们)你可以在 str 而不是 idx 上做反连接。

UPDATE(来自 ypercube 的提示)看来 OP 希望将字符串连接起来。就这样吧::

WITH ztab AS (
SELECT idx as idx
, substring ( 'WTF!' FROM idx FOR 1) as str
FROM generate_series(1, char_length( 'WTF!' )) idx
)
SELECT t1.str || t2.str AS results
FROM ztab t1
JOIN ztab t2 ON t2.idx > t1.idx
        ;

结果:

 results 
---------
 WT
 WF
 W!
 TF
 T!
 F!
(6 rows)

UPDATE2:(递归的东西来了……)

WITH RECURSIVE xtab AS (
        WITH no_cte AS (
        SELECT
        1::int AS len
        , idx as idx
        , substring ( 'WTF!' FROM idx FOR 1) as str
        FROM generate_series(1, char_length( 'WTF!' )) idx
        )
        SELECT t0.len as len
                , t0.idx
                , t0.str
        FROM no_cte t0
        UNION SELECT 1+t1.len
                , tc.idx
                , t1.str || tc.str AS str
        FROM xtab t1
        JOIN no_cte tc ON tc.idx > t1.idx
        )
SELECT * FROM xtab
ORDER BY len, str
-- WHERE len=2
        ;

结果 3:

 len | idx | str  
-----+-----+------
   1 |   4 | !
   1 |   3 | F
   1 |   2 | T
   1 |   1 | W
   2 |   4 | F!
   2 |   4 | T!
   2 |   3 | TF
   2 |   4 | W!
   2 |   3 | WF
   2 |   2 | WT
   3 |   4 | TF!
   3 |   4 | WF!
   3 |   4 | WT!
   3 |   3 | WTF
   4 |   4 | WTF!
(15 rows)

【讨论】:

Tnx!我不认为 generate_series() 函数接受文本参数(我什至没有查过),所以这个丑陋的黑客确实是一种解决方法。 什么丑陋的黑客?! (哦,Select 应该类似于 SELECT t1.str || t2.str AS resultSELECT t1.str AS str1, t2.str AS str2 是的,你当然是对的。我不喜欢 DBMS 中的字符串操作,我更喜欢根据关系模型来看待事物...... 好的,那么挑战是概括查询以接受一个参数 (n) 来表示要使用的字符数的长度。而不是你的常量 2 :) 我认为应该可以使用递归 CTE。【参考方案2】:
with chars as (
   select unnest(regexp_split_to_array('ABC','')) as c
)
select c1.c||c2.c
from chars c1
  cross join chars c2

要删除排列,您可以使用以下命令:

with chars as (
   select unnest(regexp_split_to_array('ABC','')) as c
)
select c1.c||c2.c
from chars c1
  cross join chars c2
where c1.c < c2.c

【讨论】:

这看起来比我的优雅得多 ;-( 正如我所说:我不喜欢字符串。顺便说一句,它会抑制排列吗? @wildplasser:你的意思是对 AB 和 BA 一样对待?我编辑了我的答案 这就是我的意思。类似的问题是字符串中的重复字符,例如“ABBA”。 (这在 OP 中没有解决) @wildplasser:我认为通过在内部选择查询中添加distinct 可以轻松处理重复项。这样 CTE 只返回每个字符一次 是的。但是 c1.c 【参考方案3】:

如何使用多个单词...灵感来自@wildplasser 和此来源info

WITH RECURSIVE xtab AS (
    WITH no_cte AS (
    SELECT
    1::int AS len
    , idx as idx
    , unnest(ARRAY['MY','POSTGRESQL','VERSION','9.6']) as str
    FROM generate_series(1, array_length(ARRAY['MY','POSTGRESQL','VERSION','9.6'],1)) idx
    )
    SELECT t0.len as len
            , t0.idx
            , t0.str
    FROM no_cte t0
    UNION SELECT 1+t1.len
            , tc.idx
            , t1.str ||','|| tc.str AS str
    FROM xtab t1
    JOIN no_cte tc ON tc.idx > t1.idx
    )
    SELECT distinct
    array_to_string(ARRAY(SELECT DISTINCT trim(x) FROM unnest(string_to_array(str,',')) x),', ') FROM xtab

【讨论】:

以上是关于PostgreSQL 组合无重复的主要内容,如果未能解决你的问题,请参考以下文章

Postgresq9.6主从部署

postgresql 安装

Pacemaker+Corosync搭建PostgreSQL集群

PostgreSQL备机checkpoint

算法面试题 08.07. 无重复字符串的排列组合(多语言实现)

算法面试题 08.07. 无重复字符串的排列组合(多语言实现)