Postgresql
如何計算 SQL 中輸入字元串的所有分組排列?
給定一個像“ABC”這樣的輸入,生成一個查詢,計算給定字元串的 0 個或多個潛在拆分,
所需的輸出,
A B C A BC AB C ABC
給定一個像“ABCD”這樣的輸入
A B C D A BC D A B CD AB C D A BCD AB CD ABC D ABCD
並不是所有與輸出的形成方式、數組、行、json 等有關。更多的是尋找所有分組排列的離散列表。
我想這只是一個有趣的挑戰,但這是我的解決方案:
WITH s(s) AS (VALUES ('ABCD')) SELECT substr(s, 1, 1) || string_agg( CASE WHEN i & (2::numeric ^ p)::bigint = 0 THEN '' ELSE ' ' END || substr(s, p + 2, 1), '' ) FROM s CROSS JOIN generate_series(0, (2::numeric ^ (length(s) - 1) - 1)::bigint) AS i CROSS JOIN generate_series(0, length(s) - 2) AS p GROUP BY s, i; ?column? ══════════ A BC D AB C D ABCD ABC D A B CD AB CD A B C D A BCD (8 rows)
這個想法是讓二進制數從 0 到 2 ^ (length - 1) - 1 並在有 1 的地方插入空格。所以 101(十進制 5)將變為
A BC D
.
二項式問題可以轉化為這個簡單的虛擬碼算法:
take the first letter while more letters, loop make two copies, one with trailing space append next letter end loop
遞歸函式
這可以用任何體面的過程語言用遞歸函式優雅地實現。使用 PL/pgSQL:
CREATE OR REPLACE FUNCTION word_permutations(_word text) RETURNS SETOF text LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE STRICT AS $func$ BEGIN IF length(_word) > 1 THEN RETURN QUERY SELECT left(_word, 1) || s || w FROM (VALUES (''), (' ')) sep(s) , word_permutations(right(_word, -1)) w; ELSE RETURN NEXT _word; END IF; END $func$;
稱呼:
SELECT word_permutations('ABCD');
在我的測試中表現最好。~ 比 Laurenz 查詢快 50 倍,但仍然比以下 rCTE 快 2-3 倍(有或沒有函式包裝器)。
帶有 rCTE 的純 SQL
由於這是 dba.SE,一個具有遞歸 CTE 的純 SQL 解決方案:
WITH RECURSIVE val(w) AS (SELECT 'ABCD') -- input , sep(s) AS (VALUES (''), (' ')) , cte AS ( SELECT LEFT(w, 1) AS perm, right(w, -1) AS rest FROM val UNION ALL SELECT perm || s || LEFT(rest, 1), right(rest, -1) FROM cte, sep WHERE rest <> '' ) SELECT perm FROM cte WHERE rest = '';
結果相同:
db<>fiddle here(添加了性能測試)