2009-09-25 2 views
0

У меня есть следующий рекурсивная таблица-функции в MS SQL, для того, чтобы восстановить иерархию объектов из базы данных:Состояния в рекурсивном запросе


WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr, Iteration) AS 
(
    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, 1 
    FROM Field 
    WHERE Field.ParentNum = @ParentNum 

    UNION ALL 

    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, tmpField.Iteration + 1 
    FROM Field INNER JOIN 
    tmpField on Field.ParentNum = tmpField.ChildNum 
) 
SELECT DISTINCT ParentNum AS ParentNum, ChildNum AS ChildNum, FieldNum, FieldDescr 
FROM tmpField 

Я хочу изменить его следующим образом:

в последней итерации, когда больше нет «дети», я хочу, чтобы ChildNum поле, чтобы иметь значение FieldNum. Во всех предыдущих итерациях ChildNum должно иметь значение поля ChildNum, как сейчас.

Может ли кто-нибудь предложить метод для достижения этого, используя вышеуказанный запрос в качестве отправной точки?

Обратите внимание: несмотря на свое имя, поле ChildNum не ссылается ни на какие дети из строки, но должно интерпретироваться как идентификатор этой строки.

ответ

2

, когда нет больше детей, то это означает, что ChildNum является NULL так:

... 

UNION ALL 

    SELECT Field.ParentNum, 
     COALESCE(Field.ChildNum, Field.FieldNum) ChildNum, 
     Field.FieldNum, 
     ... 

EDIT: (после Даан комментарий)

нормально, в этом случае, мы могли бы проверить на ChildNum 'дети' Количество:

... 

    UNION ALL 

     SELECT F1.ParentNum, 
      CASE WHEN (SELECT COUNT(1) 
          FROM FIELD F2 
         WHERE F2.ParentNum = F1.ChildNum) = 0 
        THEN F1.FieldNum 
        ELSE F1.ChildNum 
      END ChildNum, 
      F1.FieldNum, F1.FieldDescr, tmpField.Iteration + 1 
     FROM Field F1 INNER JOIN 
     tmpField on F1.ParentNum = tmpField.ChildNum 

... 

EDIT2:

перейдём чек снаружи:

WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr, Iteration) AS 
(
    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, 1 
    FROM Field 
    WHERE Field.ParentNum = @ParentNum 

    UNION ALL 

    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, tmpField.Iteration + 1 
    FROM Field INNER JOIN 
    tmpField on Field.ParentNum = tmpField.ChildNum 
) 
SELECT DISTINCT ParentNum AS ParentNum, 
       CASE WHEN EXISTS (SELECT NULL 
            FROM Field f 
            WHERE tmpField.ChildNum = f.ParentNum) 
         THEN tmpField.ChildNum 
         ELSE tmpField.FieldNum 
       END ChildNum, 
       FieldNum, 
       FieldDescr 
FROM tmpField 
+0

Нет, к сожалению, , ChildNum не является нулевым в этом случае. Вы должны прочитать ChildNum как идентификатор текущей строки, а не как ссылку на другую строку. Именование неловко, я знаю, но это из-за моего контроля, к сожалению :) – Daan

+0

Ваше редактирование выглядит как хороший способ выполнить это. К сожалению, при попытке изменить мою функцию он теперь дает следующую ошибку: функции GROUP BY, HAVING или aggregate не разрешены в рекурсивной части рекурсивного общего выражения таблицы «tmpField». Любые предложения об обходном пути? – Daan

+0

Вместо случая, когда (SELECT COUNT (1) ...) = 0 ТОГДА FieldNum ELSE ChildNum END следует использовать СЛУЧАЙ КОГДА EXIST (Select * из ...) ТОГДА ChildNum ELSE FieldNum END Второй работает быстрее – Niikola

1

Это должно возвращать вам данные. я удалил итерации, как вы не используете его позже

РЕГИСТРИРУЙТЕСЬ версию

;WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr) AS 
(
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    WHERE f.ParentNum = @ParentNum 
    UNION ALL 
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    INNER JOIN tmpField on f.ParentNum = tmpField.ChildNum 
) 
SELECT t.ParentNum AS ParentNum, 
     Case When p.ParentNum is Null 
      Then t.FieldNum 
      Else t.ChildNum 
     End AS ChildNum, 
     t.FieldNum, 
     t.FieldDescr 
FROM tmpField t 
Left Join (Select distinct ParentNum From Field) p on t.ChildNum=p.ParentNum 

или

подзапрос версии (модифицировано для использования EXISTS вместо COUNT)

;WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr) AS 
(
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    WHERE f.ParentNum = @ParentNum 
    UNION ALL 
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    INNER JOIN tmpField on f.ParentNum = tmpField.ChildNum 
) 
SELECT t.ParentNum AS ParentNum, 
     Case When Exists(Select * from Field p Where t.ChildNum=p.ParentNum) 
      Then t.ChildNum 
      Else t.FieldNum 
     End AS ChildNum, 
     t.FieldNum, 
     t.FieldDescr 
FROM tmpField t 
+0

Оба вышеуказанных запроса перфорации в два раза быстрее, чем решение с COUNT – Niikola

 Смежные вопросы

  • Нет связанных вопросов^_^