表值函数中的顺序错误(保持递归 CTE 的“顺序”)

发布于 2024-09-27 04:07:07 字数 2741 浏览 6 评论 0原文

几分钟前,我问这里如何使用递归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 像游标一样从一个记录迭代到另一个记录,并且以下 select 完全按照此顺序返回它。但是当我调用 TVF 时,我得到了主键 idData 的顺序。

解决方案很简单。我只需要删除 TVF 返回表的父键。因此,将...更改

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

为...

RETURNS @retPreviousClaims TABLE 
(
     idData int
)

..,它会保持正确的“顺序”(与插入 CTE 临时结果集中的顺序相同)。

更新2: 因为 Damien 提到“CTE-Order”在某些情况下可能会发生变化,所以我将在 CTE 中添加一个新列 relationLevel 来描述父记录的关系级别(顺便说一句,这完全是)对于 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

谢谢。

a few minutes ago i asked here how to get parent records with a recursive CTE.
This works now, but I get the wrong order(backwards, ordered by the PK idData) when i create a Table valued Function which returns all parents. I cannot order directly because i need the logical order provided by the CTE.

This gives the correct order(from next parent to that parent and so on):

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

But the following function returns all records in backwards order(ordered by 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);

UPDATE:
Since everybody beliefs that the CTE is not ordering(Any "ordering" will be totally arbitrary and coincidental), i'm wondering why the opposite seems to be true. I have queried a child claim with many parents and the order in the CTE is exactly the logical order when i go from child to parent and so on. This would mean that the CTE is iterating from record to record like a cursor and the following select returns it in exact this order. But when i call the TVF i got the order of the primary key idData instead.

The solution was simple. I only needed to remove the parent key of the return-Table of the TVF. So change...

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

to...

RETURNS @retPreviousClaims TABLE 
(
     idData int
)

.. and it keeps the right "order" (same order they were inserted into the CTE's temporary result set).

UPDATE2:
Because Damien mentioned that the "CTE-Order" could change in certain circumstances, i will add a new column relationLevel to the CTE which describes the level of relationship of the parent records (what is by the way quite useful in general f.e. for a ssas cube).
So the final Inline-TVF(which returns all columns) is now:

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
)

This is an exemplary relationship:

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

alt text

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

呆° 2024-10-04 04:07:07

进行 ORDERing 的正确方法是将 ORDER BY 子句添加到最外层的选择中。其他任何事情都依赖于可能随时更改的实现细节(包括数据库/表的大小是否增加,这可能允许发生更多并行处理)。

如果您需要方便的方式来进行排序,请查看 WITH 上的 MSDN 页面

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 
)

将类似内容添加到 CTE 的 EmployeeLevel 列中,一切都应该有效。

The correct way to do your ORDERing is to add an ORDER BY clause to your outermost select. Anything else is relying on implementation details that may change at any time (including if the size of your database/tables goes up, which may allow more parallel processing to occur).

If you need something convenient to allow the ordering to take place, look at Example D in the examples from the MSDN page on 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 
)

Add something similay to the EmployeeLevel column to your CTE, and everything should work.

就此别过 2024-10-04 04:07:07

我认为 CTE 正在创建排序的印象是错误的。巧合的是,行按顺序出现(可能是由于它们最初插入 tabData 的方式)。无论如何,TVF 返回一个表,因此如果您想保证排序,则必须将 ORDER BY 显式添加到您用来调用它的 SELECT 中:

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

I think the impression that the CTE is creating an ordering is wrong. It's a coincidence that the rows are coming out in order (possibly due to how they were originally inserted into tabData). Regardless, the TVF is returning a table so you have to explicitly add an ORDER BY to the SELECT you're using to call it if you want to guarantee ordering:

select * from dbo._previousClaimsByFiData(16177344) order by idData
假面具 2024-10-04 04:07:07

看不到任何 ORDER BY - 无论是在表值函数中,还是在该 TVF 的 SELECT 中。

任何“排序”都将是完全任意和巧合的。

如果您想要特定的订单,则需要指定 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...)

There is no ORDER BY anywhere in sight - neither in the table-valued function, nor in the SELECT from that TVF.

Any "ordering" will be totally arbitrary and coincidental.

If you want a specific order, you need to specify an ORDER BY.

So why can't you just add an ORDER BY to your SELECT:

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

or put your ORDER BY into the TVF:

INSERT INTO @retPreviousClaims
    SELECT idData FROM PreviousClaims
    ORDER BY idData DESC (or whatever it is you want to order by...)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文