Неправильный порядок в табличной функции (сохранить «порядок» рекурсивного CTE) - PullRequest
2 голосов
/ 14 октября 2010

несколько минут назад я спросил здесь , как получить родительские записи с рекурсивным CTE.Это работает сейчас, но я получаю неправильный порядок (в обратном порядке, упорядоченный PK idData), когда я создаю табличную функцию, которая возвращает всех родителей.Я не могу заказать напрямую, потому что мне нужен логический порядок, предоставленный CTE.

Это дает правильный порядок (от следующего родителя к этому родителю и т. Д.):

declare @fiData int;
set @fiData=16177344;
WITH PreviousClaims(idData,fiData) 
AS(
    SELECT parent.idData,parent.fiData
    FROM tabData parent
    WHERE parent.idData = @fiData

    UNION ALL

    SELECT child.idData,child.fiData
    FROM tabData child
    INNER JOIN PreviousClaims parent ON parent.fiData = child.idData
)
select iddata from PreviousClaims

Но следующееФункция возвращает все записи в обратном порядке (упорядочено по PK):

CREATE FUNCTION [dbo].[_previousClaimsByFiData] (
    @fiData INT
)

RETURNS @retPreviousClaims TABLE 
(
    idData int PRIMARY KEY NOT NULL
)
AS 
BEGIN
    DECLARE @idData int;

    WITH PreviousClaims(idData,fiData) 
    AS(
        SELECT parent.idData,parent.fiData
        FROM tabData parent
        WHERE parent.idData = @fiData

        UNION ALL

        SELECT child.idData,child.fiData
        FROM tabData child
        INNER JOIN PreviousClaims parent ON parent.fiData = child.idData
    )

    INSERT INTO @retPreviousClaims
        SELECT idData FROM PreviousClaims;
    RETURN;
END;

select * from dbo._previousClaimsByFiData(16177344);

ОБНОВЛЕНИЕ: Поскольку все считают, что CTE не упорядочивает ( Любой "упорядочивание")будет совершенно произвольным и случайным ), я задаюсь вопросом, почему противоположное кажется верным.Я запросил заявку на ребенка со многими родителями, и порядок в CTE - это вполне логичный порядок, когда я перехожу от ребенка к родителю и так далее.Это будет означать, что CTE выполняет итерацию от записи к записи, как курсор, и следующий выбор возвращает его точно в этом порядке.Но когда я позвонил в TVF, я получил порядок первичного ключа idData.

Решение было простым.Мне нужно было только удалить родительский ключ таблицы возврата TVF.Так что измените ...

RETURNS @retPreviousClaims TABLE 
(
   idData int PRIMARY KEY NOT NULL
)

на ...

RETURNS @retPreviousClaims TABLE 
(
     idData int
)

.. и он сохранит правильный «порядок» (в том же порядке, в котором они были вставлены во временный набор результатов CTE).

ОБНОВЛЕНИЕ2: Поскольку Дэмиен упомянул, что «порядок CTE» может измениться при определенных обстоятельствах, я добавлю новый столбец relationLevel в CTE, который описывает уровень взаимосвязиродительские записи (что, кстати, в общем случае полезно для куба ssas).Итак, окончательный Inline-TVF (который возвращает все столбцы) теперь:

CREATE FUNCTION [dbo].[_previousClaimsByFiData] (
    @fiData INT
)

RETURNS TABLE AS
RETURN(
    WITH PreviousClaims 
    AS(
        SELECT 1 AS relationLevel, child.*
        FROM tabData child
        WHERE child.idData = @fiData

        UNION ALL

        SELECT relationLevel+1, child.*
        FROM tabData child
        INNER JOIN PreviousClaims parent ON parent.fiData = child.idData
    )

    SELECT TOP 100 PERCENT * FROM PreviousClaims order by relationLevel
)

Это примерное соотношение:

select idData,fiData,relationLevel from dbo._previousClaimsByFiData(46600314);

alt text

Спасибо.

Ответы [ 3 ]

3 голосов
/ 15 октября 2010

Правильный способ сделать ваш ORDERing - добавить предложение ORDER BY к вашему внешнему выбору. Все остальное зависит от деталей реализации, которые могут измениться в любое время (в том числе, если размер вашей базы данных / таблиц увеличится, что может привести к увеличению параллельной обработки).

Если вам нужно что-то удобное, чтобы разрешить оформление заказа, посмотрите на Пример D в примерах со страницы MSDN страницы WITH :

WITH DirectReports(ManagerID, EmployeeID, Title, EmployeeLevel) AS 
(
    SELECT ManagerID, EmployeeID, Title, 0 AS EmployeeLevel
    FROM dbo.MyEmployees 
    WHERE ManagerID IS NULL
    UNION ALL
    SELECT e.ManagerID, e.EmployeeID, e.Title, EmployeeLevel + 1
    FROM dbo.MyEmployees AS e
        INNER JOIN DirectReports AS d
        ON e.ManagerID = d.EmployeeID 
)

Добавьте что-то похожее на столбец EmployeeLevel в CTE, и все должно работать.

2 голосов
/ 15 октября 2010

Нигде в поле зрения нет ORDER BY - ни в табличной функции, ни в SELECT из этого TVF.

Любое "упорядочение" будет абсолютно произвольным и случайным.

Если вы хотите конкретный заказ, вам нужно указать ORDER BY.

Так почему вы не можете просто добавить ORDER BY в ваш SELECT:

 SELECT * FROM dbo._previousClaimsByFiData(16177344) 
 ORDER BY (whatever you want to order by)....

или поместить свой ORDER BY в TVF:

INSERT INTO @retPreviousClaims
    SELECT idData FROM PreviousClaims
    ORDER BY idData DESC (or whatever it is you want to order by...)
2 голосов
/ 14 октября 2010

Я думаю, что впечатление, что CTE создает заказ, неправильное.Это совпадение, что строки располагаются по порядку (возможно, из-за того, как они были изначально вставлены в tabData).В любом случае, TVF возвращает таблицу, поэтому вы должны явно добавить ORDER BY к SELECT, который вы используете для вызова, если вы хотите гарантировать порядок:

select * from dbo._previousClaimsByFiData(16177344) order by idData
...