2010-07-01 2 views
6

Я пытаюсь выполнить то, что, как я считаю, сложная рекурсия с использованием CTE, - это SQL Server 2008. Я не могу обернуть голову вокруг этого.SQL Server 2008 CTE Recursion

В приведенных ниже примерах вы можете принять фиксированную глубину 3 ... ничто никогда не будет ниже этого. В реальной жизни глубина «глубже», но все же исправлена. В этом примере я попытался упростить его.

Мои входные данные подобны приведенным ниже.

ID  PARENT_ID  NAME   DEPTH 
------------------------------------------ 
1  NULL   A    1 
2  1    B    2 
3  2    C    3 
4  1    D    2 

Выходной сигнал моего CTE должен быть следующей таблицей.

LEVEL1_ID LEVEL2_ID LEVEL3_ID LEVEL1_NAME LEVEL2_NAME LEVEL3_NAME 
-------------------------------------------------------------------------------- 
1   NULL   NULL   A    NULL   NULL 
1   2   NULL   A    B    NULL 
1   2   3   A    B    C 
1   4   NULL   A    D    NULL 

Если я могу получить столбцы идентификаторов на выходе, я могу, конечно, сопоставить имена в таблице поиска.

Я открыт для других способов решения этой задачи, в том числе с использованием SSIS.

+0

Я не мог понять, что логика вы подаете, глядя на свой выход. Я думаю, что последняя строка должна быть '/ B/D', потому что родительский идентификатор' D' равен 2 i.e. 'B' – IsmailS

+0

только что обновил вопрос. Прости. – thomas

+0

Снова то же самое. В строке №4, почему вам нужен Level1_ID как «1», когда его родительский идентификатор равен 2? – IsmailS

ответ

9

Не так уж трудно сделать:

;WITH cte AS 
(
    SELECT CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY ID 

дает мне выход:

/A 
/A/B 
/A/B/C 
/A/D 

В качестве побочного примечание: «глубину» можно легко вычислить по КТР и Вам не обязательно нужно хранить, что в таблице (см Level колонку я добавил):

;WITH cte AS 
(
    SELECT 
     CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID, 
     1 AS 'Level' 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT 
     CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID, 
     cte.Level + 1 AS 'Level' 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY Level, ID 
+1

Это потрясающе. благодаря! Я немного изменил вопрос, но это отлично. Я могу, конечно, использовать этот пример. – thomas

+0

Великий marc_s! Так быстро!!!! – IsmailS

+0

С помощью этого кода у вас нет проблемы с присоединением уровней несколько раз? Например, во время первого прогона рекурсивного прогона вы присоединитесь к уровням 1 с уровнями 2, во время второго запуска вы снова присоедините уровни 1 к уровням 2 и уровням 2 с уровнями 3 и т. Д. Дублированные строки «удалены» b оператором UNION, но он будет делать много дубликатов. – munissor

0

I d не помню, вы можете сделать подзапрос в cte.

У меня нет копии SQL-сервере, но вы можете попробовать с этим кодом:

WITH cte(id, path, level) 
AS 
(
    SELECT id, '/' + name, level 
    FROM yourtable 
    WHERE level = 1 

    UNION ALL 

    SELECT y.id, c.name + '/' + y.name, y.level 
    FROM yourtable y INNER JOIN 
    cte c ON c.id = y.parent_id 
    WHERE level = (SELECT max(level)+1 from cte) 
) 
SELECT path from cte 
+0

Я получаю массу ошибок: Msg 207, уровень 16, состояние 1, строка 6 Недопустимое имя столбца 'level'. Msg 207, уровень 16, состояние 1, строка Недопустимое название столбца «уровень». Msg 253, уровень 16, состояние 1, строка 1 Рекурсивный член общего выражения таблицы 'cte' имеет несколько рекурсивных ссылок. –

+1

Итак, вы не можете использовать подзапрос;) – munissor

-1
;WITH Vals AS (
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE NULL END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE NULL END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE NULL END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE NULL END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE NULL END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE NULL END 'LEVEL3_NAME', 
       ID 'PRMID'     
     FROM #Table1 
     WHERE parentId IS NULL 
     UNION ALL 
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE LEVEL1_ID END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE LEVEL2_ID END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE LEVEL3_ID END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE LEVEL1_NAME END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE LEVEL2_NAME END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE LEVEL3_NAME END 'LEVEL3_NAME', 
       ID 'PRMID'        
     FROM #Table1 inner join Vals on #Table1.parentId=PRMID 

) 

SELECT * from Vals 
Смежные вопросы