2

Мне нужно объединить имя в рекурсивном перекрестном соединении. Я не знаю, как это сделать, я пробовал CTE, используя WITH RECURSIVE, но не добился успеха.concatenate recursive cross join

У меня есть таблица вроде этого:

group_id | name 
--------------- 
13  | A 
13  | B 
19  | C 
19  | D 
31  | E 
31  | F 
31  | G 

Желаемая выход:

combinations 
------------ 
ACE 
ACF 
ACG 
ADE 
ADF 
ADG 
BCE 
BCF 
BCG 
BDE 
BDF 
BDG 

Конечно, результаты должны умножаться, если добавить 4-й (или более) группы.

ответ

2

Native Postgresql Синтаксис:

SqlFiddleDemo

WITH RECURSIVE cte1 AS 
(
    SELECT *, DENSE_RANK() OVER (ORDER BY group_id) AS rn 
    FROM mytable 
),cte2 AS 
(
    SELECT 
    CAST(name AS VARCHAR(4000)) AS name, 
    rn 
    FROM cte1 
    WHERE rn = 1 
    UNION ALL 
    SELECT 
    CAST(CONCAT(c2.name,c1.name) AS VARCHAR(4000)) AS name 
    ,c1.rn 
    FROM cte1 c1 
    JOIN cte2 c2 
    ON c1.rn = c2.rn + 1 
) 
SELECT name as combinations 
FROM cte2 
WHERE LENGTH(name) = (SELECT MAX(rn) FROM cte1) 
ORDER BY name; 

До:

Я надеюсь, что если вы не возражаете, что я использую SQL Server Синтаксис:

Пример:

CREATE TABLE #mytable(
    ID  INTEGER NOT NULL 
    ,TYPE  VARCHAR(MAX) NOT NULL 
); 
INSERT INTO #mytable(ID,TYPE) VALUES (13,'A'); 
INSERT INTO #mytable(ID,TYPE) VALUES (13,'B'); 
INSERT INTO #mytable(ID,TYPE) VALUES (19,'C'); 
INSERT INTO #mytable(ID,TYPE) VALUES (19,'D'); 
INSERT INTO #mytable(ID,TYPE) VALUES (31,'E'); 
INSERT INTO #mytable(ID,TYPE) VALUES (31,'F'); 
INSERT INTO #mytable(ID,TYPE) VALUES (31,'G'); 

Основной запрос:

WITH cte1 AS 
(
    SELECT *, rn = DENSE_RANK() OVER (ORDER BY ID) 
    FROM #mytable 
),cte2 AS 
(
    SELECT 
    TYPE = CAST(TYPE AS VARCHAR(MAX)), 
    rn 
    FROM cte1 
    WHERE rn = 1 
    UNION ALL 
    SELECT 
    [Type]  = CAST(CONCAT(c2.TYPE,c1.TYPE) AS VARCHAR(MAX)) 
    ,c1.rn 
    FROM cte1 c1 
    JOIN cte2 c2 
    ON c1.rn = c2.rn + 1 
) 
SELECT * 
FROM cte2 
WHERE LEN(Type) = (SELECT MAX(rn) FROM cte1) 
ORDER BY Type; 

LiveDemo

я предполагал, что порядок "перекрестного соединения" зависит от возрастания ID.

  • cte1 генерировать DENSE_RANK(), потому что ваши идентификаторы содержат пробелы
  • cte2 рекурсивная часть с CONCAT
  • основной запрос просто отфильтровать нужную длину и сортировать строки
+0

Awesome !! Это делает трюк. Ваше предположение о порядке правильное для этого примера ;-) в действительности я добавил столбец последовательности отображения ;-) – frankhommers

1

рекурсивный запрос немного проще в Postgres:

WITH RECURSIVE t AS ( -- to produce gapless group numbers 
    SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name 
    FROM tbl 
    ) 
, cte AS (
    SELECT grp, name 
    FROM t 
    WHERE grp = 1 

    UNION ALL 
    SELECT t.grp, c.name || t.name 
    FROM cte c 
    JOIN t ON t.grp = c.grp + 1 
    ) 
SELECT name AS combi 
FROM cte 
WHERE grp = (SELECT max(grp) FROM t) 
ORDER BY 1; 

Основная логика такая же, как и в SQL Server version provided by @lad2025, я добавил несколько незначительных улучшений.

Или вы можете использовать простой вариант если ваше максимальное количество групп не является слишком большим (не может быть очень большой, на самом деле, так как результирующий набор растет экспоненциально). Не более 5 групп:

WITH t AS ( -- to produce gapless group numbers 
    SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name AS n 
    FROM tbl 
    ) 
SELECT concat(t1.n, t2.n, t3.n, t4.n, t5.n) AS combi 
FROM  (SELECT n FROM t WHERE grp = 1) t1 
LEFT JOIN (SELECT n FROM t WHERE grp = 2) t2 ON true 
LEFT JOIN (SELECT n FROM t WHERE grp = 3) t3 ON true 
LEFT JOIN (SELECT n FROM t WHERE grp = 4) t4 ON true 
LEFT JOIN (SELECT n FROM t WHERE grp = 5) t5 ON true 
ORDER BY 1; 

Вероятно, быстрее для нескольких групп. LEFT JOIN .. ON true делает эту работу, даже если более высокие уровни отсутствуют. concat() игнорирует значения NULL. Тест с EXPLAIN ANALYZE.

SQL Fiddle показаны оба.

+0

Спасибо. Мне нужен рекурсивный запрос для моей цели. Ваша рекурсивная версия действительно немного чище. Думаю, я буду использовать это, на мой взгляд. – frankhommers