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

Sql server 表值函数的顺序错误(保持递归CTE的“顺序”),sql-server,sql-server-2005,common-table-expression,user-defined-functions,Sql Server,Sql Server 2005,Common Table Expression,User Defined Functions,几分钟前,我问如何使用递归CTE获取父记录。 现在可以了,但是当我创建一个返回所有父函数的表值函数时,我得到了错误的顺序(向后,由PK idData排序)。我不能直接订购,因为我需要CTE提供的逻辑顺序 这给出了正确的顺序(从下一个父级到该父级,依此类推): 但下面的函数将按向后顺序(按主键顺序)返回所有记录: 更新: 既然每个人都认为CTE不是有序的(任何“有序”都是完全武断和巧合的),我想知道为什么事实正好相反。我曾向许多家长询问过一个儿童索赔,CTE中的顺序正是我从一个孩子到另一个家长的

几分钟前,我问如何使用递归CTE获取父记录。 现在可以了,但是当我创建一个返回所有父函数的表值函数时,我得到了错误的顺序(向后,由PK idData排序)。我不能直接订购,因为我需要CTE提供的逻辑顺序

这给出了正确的顺序(从下一个父级到该父级,依此类推):

但下面的函数将按向后顺序(按主键顺序)返回所有记录:


更新: 既然每个人都认为CTE不是有序的(任何“有序”都是完全武断和巧合的),我想知道为什么事实正好相反。我曾向许多家长询问过一个儿童索赔,CTE中的顺序正是我从一个孩子到另一个家长的逻辑顺序,以此类推。这意味着CTE像光标一样从一个记录迭代到另一个记录,下面的select将以完全相同的顺序返回它。但是当我调用TVF时,我得到了主键idData的顺序

解决办法很简单。我只需要删除TVF返回表的父键。所以改变

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

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

更新2: 因为Damien提到“CTE顺序”在某些情况下可能会发生变化,所以我将在CTE中添加一个新的列
relationLevel
,该列描述父记录的关系级别(顺便说一下,对于ssas多维数据集来说,这在一般的f.e中非常有用)。 因此,最终的内联TVF(返回所有列)现在是:

这是一种典型的关系:

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


谢谢。

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

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

任何地方都看不到
ORDER BY
——无论是在表值函数中,还是在该TVF的
SELECT

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

如果需要特定的订单,则需要按指定订单

那么,为什么您不能将ORDER BY添加到您的SELECT:

 SELECT * FROM dbo._previousClaimsByFiData(16177344) 
 ORDER BY (whatever you want to order by)....
或者将您的
订单按
放入TVF:

INSERT INTO @retPreviousClaims
    SELECT idData FROM PreviousClaims
    ORDER BY idData DESC (or whatever it is you want to order by...)

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

如果您需要一些方便的方式来进行订购,请查看以下示例中的示例D:


在CTE的EmployeeLevel列中添加一些类似的内容,一切都应该正常。

问题在于,按idData排序的顺序毫无价值,因为不能保证它与逻辑父子顺序相等。我是否必须使用游标而不是CTE来迭代它?看看是否可以找到一种非游标方法来查找排序。我还不太了解你的数据关系,所以无法说出这一点。也许一些示例父/子数据和正确的排序可能会有所帮助。上面提到的前一个问题中提供了一个示例,下面是链接:为什么我害怕再次使用order by,尽管该顺序应该由子/父关系给出(我假设):应该没有必要订购CTE之前已经迭代过的内容(如果在CTE中任意,我应该使用光标)。也谢谢你,明天我将检查marc_的建议,该建议与你的类似。因为顺序是由父子关系的内部结构给出的。foreignkey fiData设置在每晚导入新数据时运行的存储过程中。我能看到的唯一方法(没有游标)是使用这个存储过程的orderby,但这意味着大量开销,这会减慢每个查询的速度,并使整个方法变得多余。看看我的这个老问题,你可以找到这个逻辑和顺序的一部分:经过重新思考,我得出结论,这是唯一的方法,并没有我最初认为的那么糟糕,因为只有少数父母需要顺序。好的,我需要更多的日期栏,而不仅仅是PK。谢谢,我明天会测试它。不过请注意,在INSERT中添加orderby基本上是不可行的@以前的索赔没有顺序。ORDER BY必须应用于
select*from dbo.\u先前的claimsbyfidata(…)ORDER BY…
。如果在查询上下文中使用TVF,则必须将ORDER BY应用于查询。等等等等。只有结果才有顺序。既然我们在讨论这个主题,TVF最好是内联TVF和更好的模式绑定。我指的是它们插入CTE临时结果集中的“内部”顺序。这似乎与我使用游标在记录之间迭代时的情况相同。第一条记录的idData=passed fiData,最后一条记录的fiData=NULL。但是,如果不能保证将来会保持不变,我将按照damian的建议按“关系级别”订购。您发布的解决方案现在在今天的服务器、service pack和数据库上可能适用于您。但这并不能保证。保证结果集顺序的唯一方法是在最外层的SELECT语句中指定ORDER BY子句。是的,看起来更好-最终输出仍然依赖于实现细节(INSERT中的“ORDER BY”与检索顺序有任何关系),但如果需要的话,至少您可以在这个外部选择上使用排序列来进行排序。当我
select idData,fiData,relationLevel from dbo._previousClaimsByFiData(46600314);
select * from dbo._previousClaimsByFiData(16177344) order by idData
 SELECT * FROM dbo._previousClaimsByFiData(16177344) 
 ORDER BY (whatever you want to order by)....
INSERT INTO @retPreviousClaims
    SELECT idData FROM PreviousClaims
    ORDER BY idData DESC (or whatever it is you want to order by...)
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 
)