简体   繁体   中英

PostgreSQL combinations without repetitions

How to make function in postgres that would take string or array and return all combinations of some length?

For example you have ABC and you want to get combinations with 2 characters, the result shoul be:

AB AC BC

Thank you in advance for your help.

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
        ;

Result:

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

Unfortunately I cannot find a way to avoid the double string constant. (but the whole thing could be packed into a function) If there are no duplicate characters (or you want to suppres them) you could do the anti-join on the str instead of the idx.

UPDATE (hint from ypercube) It appears that the OP wants the strings to be concatenated. So be it::

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:

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

UPDATE2: (here comes the recursive thingy...)

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
        ;

Results 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)
with chars as (
   select unnest(regexp_split_to_array('ABC','')) as c
)
select c1.c||c2.c
from chars c1
  cross join chars c2

To remove permutations you can use the following:

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

How to works with multiple words... inspired from @wildplasser and from this source 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM