Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 369
ffox003 Avatar asked Apr 11 '12 18:04

ffox003


2 Answers

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
like image 40
a_horse_with_no_name Avatar answered Nov 19 '22 06:11

a_horse_with_no_name


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)
like image 147
wildplasser Avatar answered Nov 19 '22 08:11

wildplasser