2012-04-11 21 views
5

Wie funktioniert man in Postgres, die String oder Array aufnehmen und alle Kombinationen mit einer bestimmten Länge zurückgeben?PostgreSQL-Kombinationen ohne Wiederholungen

Zum Beispiel Sie ABC haben und Sie möchten Kombinationen mit 2 Zeichen bekommen, shoul das Ergebnis sein:

AB AC BC

Sie für Ihre Hilfe Vielen Dank im Voraus.

+0

Warum Sie, dass in einem DBMS tun würde? Können Sie das nicht auf einer Anwendungsebene tun? –

+0

Ich könnte in einem Sudoku-Solver nützlich sein ;-) – wildplasser

+0

Ich würde dies in mehreren Programmiersprachen und nur eine Datenbank PostgreSQL verwenden, also dachte ich, dass dies der einfachste Weg wäre, dies an einer Stelle zu haben. Dies muss nicht zu einer Zeichenfolge hinzugefügt werden, ich gebe nur eine Umfrage, wie sollte funktionieren. – ffox003

Antwort

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 
     ; 

Ergebnis:

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

Leider kann ich nicht einen Weg finden, den Doppel-String konstant zu vermeiden. (aber die ganze Sache könnte in eine Funktion gepackt werden) Wenn es keine doppelten Zeichen gibt (oder du sie unterdrücken willst), könntest du den Anti-Join auf der Str anstelle der IDX machen.

UPDATE (Hinweis von Ypercube) Es scheint, dass das OP möchte die Zeichenfolgen verkettet werden. So seien es ::

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 
     ; 

Ergebnisse:

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

UPDATE2: (hier kommt das rekursive Dingen ...)

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 
     ; 

Ergebnisse 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

Schöne Lösung! –

+0

Tnx! Ich glaube nicht, dass die Funktion generate_series() Textargumente akzeptiert (ich habe das nicht mal nachgeschlagen), also ist dieser hässliche Hack wirklich ein Weg um dies zu umgehen. – wildplasser

+0

Welcher hässliche Hack? (Oh, und das Select sollte etwas wie "SELECT t1.str || t2.str AS Ergebnis" oder "SELECT t1.str AS str1, t2.str AS str2") sein –

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 

Um Permutationen entfernen Sie verwenden können, die folgenden:

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

Das sieht viel eleganter aus als meines ;-(wie gesagt: ich mag keine Saiten. BTW unterdrückt die Permutationen? – wildplasser

+0

@wildplasser: Du meinst AB und BA gleich behandeln?Ich habe meine Antwort bearbeitet –

+0

Das habe ich gemeint. Ein ähnliches Problem wären doppelte Zeichen in der Zeichenfolge, z. B. "ABBA". (das wurde nicht im OP angesprochen) – wildplasser

0

Wie arbeitet mit mehreren Worten ... von @wildplasser inspiriert und aus dieser Quelle 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 
Verwandte Themen