2012-04-11 33 views
5

Làm thế nào để thực hiện chức năng trong postgres mà sẽ có chuỗi hoặc mảng và trả về tất cả các kết hợp của một số chiều dài?Kết hợp PostgreSQL không lặp lại

Ví dụ, bạn có ABC và bạn muốn để có được kết hợp với 2 ký tự, kết quả shoul là:

AB AC BC

Cảm ơn bạn trước sự giúp đỡ của bạn.

+0

Tại sao bạn sẽ làm điều đó trong một DBMS? Bạn không thể làm điều đó ở cấp ứng dụng? –

+0

Tôi có thể sử dụng trong một sudoku-solver ;-) – wildplasser

+0

Tôi sẽ sử dụng nó trong nhiều ngôn ngữ lập trình và chỉ một cơ sở dữ liệu PostgreSQL, vì vậy tôi đã nghĩ rằng đây sẽ là cách dễ nhất để có điều này ở một nơi. Điều này không cần phải tham gia chuỗi, tôi chỉ cung cấp cho sugestion cách hoạt động. – ffox003

Trả lời

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 
     ; 

Kết quả:

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

Đáng tiếc là tôi không thể tìm thấy một cách để tránh các chuỗi kép liên tục. (nhưng toàn bộ điều có thể được đóng gói vào một chức năng) Nếu không có nhân vật trùng lặp (hoặc bạn muốn suppres chúng) bạn có thể làm chống tham gia trên đường thay vì idx.

CẬP NHẬT (gợi ý từ ypercube) Dường như OP muốn các chuỗi được ghép nối. Vì vậy, có thể là ::

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 
     ; 

Kết quả:

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

UPDATE2: (ở đây có thingy đệ quy ...)

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 
     ; 

Kết quả 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

Giải pháp tuyệt vời! –

+0

Tnx! Tôi không nghĩ rằng hàm generate_series() chấp nhận các đối số văn bản (tôi thậm chí không nhìn lên), vì vậy hack xấu xí này thực sự là một cách xung quanh đó. – wildplasser

+0

Điều gì xấu xí hack?! (oh, và Select nên là một cái gì đó giống như 'SELECT t1.str || t2.str AS result' hoặc' 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 

Để loại bỏ các hoán vị có thể sử dụng như sau:

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

Điều đó có vẻ thanh lịch hơn rất nhiều so với tôi ;-(như tôi đã nói: Tôi không thích dây. BTW có ngăn chặn các hoán vị không? – wildplasser

+0

@ wildplasser: bạn có nghĩa là điều trị AB và BA như nhau?Tôi đã chỉnh sửa câu trả lời của mình –

+0

Đó là ý của tôi. Một vấn đề tương tự sẽ là các ký tự trùng lặp trong chuỗi, chẳng hạn như "ABBA". (điều này đã không được giải quyết trong OP) – wildplasser

0

Làm thế nào để làm việc với nhiều từ ... lấy cảm hứng từ @wildplasser và từ nguồn này 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 
Các vấn đề liên quan