查找所有相关记录

时间:2010-02-09 18:03:37

标签: sql sql-server

我有一个Order表,它有一个LinkedOrderID字段。

我想构建一个查找所有链接订单的查询,并在结果集中返回它们。

从[Order]中选择OrderID,LinkOrderID,其中LinkOrderID不为空

订单ID LinkOrderID
787016 787037
787037 787786
787786 871702


我想要一个返回以下内容的存储过程:
OrderID InheritanceOrder
787016 1
787037 2
787786 3
871702 4

我还想确保我没有无限循环

3 个答案:

答案 0 :(得分:3)

DECLARE @Order TABLE (OrderID INT NOT NULL, LinkOrderID  INT NOT NULL)

INSERT
INTO    @Order
VALUES  (787016, 787037)

INSERT
INTO    @Order
VALUES  (787037, 787786)

INSERT
INTO    @Order
VALUES  (787786, 871702)

/*
INSERT
INTO    @Order
VALUES  (871702, 787016)
*/

;WITH    q (OrderId, LinkOrderId, InheritanceOrder, FirstItem) AS
        (
        SELECT  OrderID, LinkOrderId, 1, OrderID
        FROM    @Order
        WHERE   OrderID = 787786
        UNION ALL
        SELECT  o.OrderId, o.LinkOrderId, q.InheritanceOrder + 1, q.FirstItem
        FROM    q
        JOIN    @Order o
        ON      o.OrderID = q.LinkOrderId
        WHERE   o.OrderID <> q.FirstItem
        UNION ALL
        SELECT  LinkOrderId, NULL, q.InheritanceOrder + 1, q.FirstItem
        FROM    q
        WHERE   q.LinkOrderID NOT IN
                (
                SELECT  OrderID
                FROM    @Order
                )
        )
SELECT  OrderID, InheritanceOrder
FROM    q
ORDER BY
        InheritanceOrder

这假设OrderIDLinkOrderID都是唯一的(即,它是链接列表,而不是树)。

也可以使用最后一个未注释的插入(这会产生一个循环)

答案 1 :(得分:0)

要检查无限循环,有两项检查:

首先,请确保以LinkOrderID

中出现的_id开头
select o1.OrderID from Order o1
left outer join Order o2 on o1.OrderId = o2.LinkOrderID
where o2.LinkOrderID is null;

这将为您提供一个列表,它是链接列表的开头。

然后,确保你的所有_ids都不会出现过多次。

select * from {
  select LinkOrderId, count(*) as cnt from Order
} where cnt > 1;

如果这两个条件属实(您从一个从未出现在链接列表中的订单开始,并且您没有多次链接的OrderIds),那么 不能 < / strong>有一个循环。

答案 2 :(得分:0)

Sweet:我发现这个解决方案归功于Quassnoi的精彩代码。我把它调整到第一步走向最老的父母,然后走过所有的孩子。再次感谢!

-- =============================================
-- Description: Gets all LinkedOrders for OrderID. 
-- It will first walk up and find oldest linked parent, and then next walk down recursively and find all children.
-- =============================================
alter PROCEDURE Order_Order_GetAllLinkedOrders
(
    @StartOrderID int
)
AS
--Step#1: find oldest parent
DECLARE @oldestParent int
;WITH    vwFirstParent (OrderId) AS 
        ( 
        SELECT  OrderID
        FROM    [Order]
        WHERE   OrderID = @StartOrderID 
        UNION ALL 
        SELECT  o.OrderId
        FROM    vwFirstParent
        JOIN    [Order] o 
        ON      o.LinkOrderID = vwFirstParent.OrderId 
        )

select @oldestParent = OrderID from vwFirstParent 

--Step#2: find all children, prevent recursion
;WITH    q (OrderId, LinkOrderId, InheritanceOrder, FirstItem) AS 
        ( 
        SELECT  OrderID, LinkOrderId, 1, OrderID 
        FROM    [Order]
        WHERE   OrderID = @oldestParent 
        UNION ALL 
        SELECT  o.OrderId, o.LinkOrderId, q.InheritanceOrder + 1, q.FirstItem 
        FROM    q 
        JOIN    [Order] o 
        ON      o.OrderID = q.LinkOrderId 
        WHERE   o.OrderID <> q.FirstItem 
        UNION ALL 
        SELECT  LinkOrderId, NULL, q.InheritanceOrder + 1, q.FirstItem 
        FROM    q
        WHERE   q.LinkOrderID NOT IN 
                ( 
                SELECT  OrderID 
                FROM    [Order]
                )
        ) 
SELECT  OrderID,LinkOrderId,  InheritanceOrder
FROM    q