2012-04-11 7 views
5

Come rendere la funzione in postgres che richiede stringa o matrice e restituisce tutte le combinazioni di una certa lunghezza?Combinazioni PostgreSQL senza ripetizioni

Per esempio si dispone di ABC e si desidera ottenere combinazioni con 2 caratteri, il risultato shoul essere:

AB AC aC

vi ringrazio in anticipo per il vostro aiuto.

+0

Perché l'hai fatto in un DBMS? Non puoi farlo a livello di applicazione? –

+0

Potrei essere utile in un risolutore di sudoku ;-) – wildplasser

+0

Lo userei in più linguaggi di programmazione e solo un database PostgreSQL, quindi stavo pensando che questo sarebbe stato il modo più semplice per avere questo in un unico posto. Questo non deve essere unito a una stringa, ho appena dato sugestione come dovrebbe funzionare. – ffox003

risposta

9
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 
     ; 

Risultato:

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

Purtroppo non riesco a trovare un modo per evitare la doppia stringa costante. (ma l'intera cosa può essere impacchettata in una funzione) Se non ci sono caratteri duplicati (o se si desidera supprutarli) si potrebbe fare l'anti-join sullo str invece dell'idx.

UPDATE (suggerimento da ypercube) Sembra che l'OP desideri che le stringhe siano concatenate. Così sia ::

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 
     ; 

Risultati:

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

Update2: (qui viene il thingy ricorsiva ...)

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 
     ; 

Risultati 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) 
+0

Bella soluzione! –

+0

Tnx! Non credo che la funzione generate_series() accetti argomenti di testo (non l'ho nemmeno visto), quindi questo brutto hack è davvero un modo per aggirare questo. – wildplasser

+0

Che brutto trucco ?! (oh, e Select dovrebbe essere qualcosa come 'SELECT t1.str || t2.str AS result' o' SELECT t1.str AS str1, t2.str AS str2') –

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

Per rimuovere permutazioni è possibile utilizzare il seguente:

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 
+0

Questo sembra molto più elegante del mio ;-(come ho detto: non mi piacciono gli archi. BTW sopprime le permutazioni? – wildplasser

+0

@wildplasser: vuoi dire trattare allo stesso modo AB e BA?Ho modificato la mia risposta –

+0

Ecco cosa intendevo. Un problema simile potrebbe essere rappresentato da caratteri duplicati nella stringa, ad esempio "ABBA". (questo non è stato risolto nell'OP) – wildplasser

0

Come funziona con più parole ... ispirato da @wildplasser e da questa fonte 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