2

几分钟前,我问here如何获得递归CTE的父记录。 现在可以使用,但是当我创建一个返回所有父母的表值函数时,我得到了错误的顺序(向后,由PK idData排序)。我不能直接订购,因为我需要CTE提供的逻辑顺序。表值函数的错误订单(保留递归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的临时结果集)。

UPDATE2: 因为达明提到了“CTE-令”可能在某些情况下发生变化,我将一个新列relationLevel添加到CTE描述的父记录关系的水平(什么是由对于一个ssas立方体来说,这个方法在一般情况下非常有用) 所以最终的内联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

谢谢。

+0

您发布的解决方案*现在可以在当今的服务器,Service Pack和数据库上为您工作。但它不能得到保证。确保结果集排序的唯一方法是在最外面的SELECT语句中指定一个ORDER BY子句。 – 2010-10-15 08:22:12

+0

是的,看起来更好 - 最终输出仍然依赖于实现细节(即INSERT中的“ORDER BY”与检索顺序有任何关系),但至少您有排序列可用于执行ORDER BY这个外层选择,如果你需要的话。 – 2010-10-15 09:14:12

+0

当我将relationLevel定义为主键时它有什么不同吗?那么是否有序保证,并不需要应用于外部选择? – 2010-10-15 10:18:31

回答

3

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

如果你需要的东西方便,允许排序发生,可查看例d的例子从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 
) 

到EmployeeLevel列添加一些similay到您的CTE,一切都应该工作。

+0

谢谢你的提示。结果保持不变,但是当你说它可能在某些情况下稍后改变时,我将使用此方法强制执行正确的顺序。我会尽快更新我的问题。 – 2010-10-15 08:53:40

2

我认为CTE创建订单的印象是错误的。这是巧合的,行是按顺序出来的(可能是由于它们最初是如何插入到tabData中的)。无论如何,所以你必须明确地添加一个ORDER BY的SELECT你使用调用它,如果你想保证订货TVF返回一个表:

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

问题是,idData的顺序毫无价值,因为不能保证它与逻辑父子顺序相等。我是否必须使用游标而不是使用CTE来遍历它? – 2010-10-14 19:43:08

+1

查看您是否可以找到非游标方法来查找排序。不知道你的数据关系是否足以说明这一点。也许一些样本父母/孩子的数据和正确的排序可能会有所帮助。 – Anna 2010-10-14 20:05:17

+0

在前面提到的问题中提供了一个示例,这里是链接:http://stackoverflow.com/questions/3935005/recursive-cte-to-find-parent-records – 2010-10-14 20:57:24

2

没有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...) 
+1

因为顺序是由父子关系的内部结构给出的。 foreignkey fiData在每天晚上导入新数据时运行的存储过程中设置。我可以看到(没有游标)的唯一方法是使用此存储过程的顺序,但这意味着很多开销会减慢每个查询的速度,并使整个方法变得冗余。看看我的这个老问题,你可以找到这个逻辑的一部分和顺序:http://stackoverflow.com/questions/3266299/whats-wrong-with-my-sql-find-a-previous-记录 – 2010-10-14 21:08:34

+1

重新思考后,我得出结论,这是唯一的方法,并没有我想象的那么糟糕,因为只有少数父母可以订购。好吧,我需要更多日期列而不是只有PK。谢谢,我明天会测试它。 – 2010-10-14 21:30:49

+1

请注意,在INSERT中添加ORDEr BY基本上是无操作。 @retPreviousClaims没有顺序。 ORDER BY必须应用于'select * from dbo._previousClaimsByFiData(...)ORDER BY ....'。如果在查询上下文中使用TVF,则必须将ORDER BY应用于查询。等等等等。只有**结果**才有秩序。 – 2010-10-14 23:30:38