如何在父/子查询中添加显示顺序?

时间:2015-03-26 21:19:09

标签: sql-server sql-server-2008 tsql

简单的表格示例:

CatID  |  ParentID  |  Name  |  DisplayOrder
============================================
1         NULL         A        1
2         1            A1       1
3         1            A2       2
4         NULL         B        2

etc.

我想由父母和父母的孩子订购。但我也想遵守DisplayOrder。因此,首先由父级按显示顺序,然后按每个父级内的子项,按其显示顺序。只有一层深。我希望尽可能避免复杂的查询,例如我见过的大多数CTE示例。

嗯,这可行

SELECT * FROM (
    SELECT      

     CatID
    ,ParentID
    ,DisplayOrder AS LevelOne
    ,NULL AS LevelTwo

    FROM Table WHERE ParentID IS NULL

    UNION ALL

    SELECT 
       c.CatID
      ,c.ParentID
      ,d.DisplayOrder AS LevelOne
      ,c.DisplayOrder AS LevelTwo

    FROM Table c WHERE ParentID IS NOT NULL
    INNER JOIN Table d ON c.ParentID = d.CatID
) AS Cats
ORDER BY Cats.LevelOne, Cats.LevelTwo

好的,所以上面的查询工作正常,但只在一个条件下...父母的DisplayOrder必须是不同的。因此,如果两个父母将1作为DisplayOrder(无论出于何种原因),那么结果集就会变得非常混乱。只要我按顺序显示父级的显示顺序,1,2,3,4,5等,然后它就可以了。我理解为什么,但试图找到解决它的最佳解决方案,并考虑重复显示订单的可能性。

2 个答案:

答案 0 :(得分:1)

<强> SQL2008 +:

一种解决方案是使用嵌套集的HIERARCHYID(最小SQL2008)数据类型将邻接列表(CatID, ParentID)转换为嵌套集(/ParentID/.../CatID/)。要对HIERARCHYID值进行排序,SQL Server使用深度优先方法(父级 - >所有子级)。

DECLARE @Nodes TABLE (
    CatID       INT PRIMARY KEY,
    ParentID    INT NULL, -- FK
    Name        VARCHAR(50)
);
INSERT  @Nodes
VALUES  
(111, NULL, 'A'), (22, 111, 'A1'), (33, 111, 'A2'), (44, 111, 'A3'), 
(222, NULL, 'B'), (55, 222, 'B2'), (66, 222, 'B1');

WITH RecursiveCTE
AS (
    SELECT  crt.CatID, crt.ParentID, crt.Name, 
            CONVERT(HIERARCHYID, '/'+CONVERT(VARCHAR(11), crt.CatID)+'/') AS HID
    FROM    @Nodes crt
    WHERE   crt.ParentID IS NULL
    UNION ALL 
    SELECT  crt.CatID, crt.ParentID, crt.Name, 
            CONVERT(HIERARCHYID, prev.HID.ToString()+CONVERT(VARCHAR(11), crt.CatID)+'/') AS HID
    FROM    @Nodes crt INNER JOIN RecursiveCTE prev ON crt.ParentID = prev.CatID
    WHERE   crt.ParentID IS NOT NULL
)
SELECT  *, 
        HID.ToString() AS HID_ToString, 
        ROW_NUMBER() OVER(PARTITION BY HID.GetLevel() ORDER BY HID) AS DisplayOrder1,
        ROW_NUMBER() OVER(PARTITION BY HID.GetAncestor(1) ORDER BY HID) AS DisplayOrder2,
        HID.GetLevel() AS HID_Level
FROM    RecursiveCTE
ORDER BY HID
OPTION (MAXRECURSION 200); -- Default MAXRECURSION value is 100

输出:

/*
CatID ParentID Name HID        HID_ToString DisplayOrder1 DisplayOrder2 HID_Level
----- -------- ---- ---------- ------------ ------------- ------------- ---------
111   NULL     A    0xE02FC0   /111/        1             1             1
22    111      A1   0xE02FF074 /111/22/     1             1             2
33    111      A2   0xE02FF24C /111/33/     2             2             2
44    111      A3   0xE02FF2E4 /111/44/     3             3             2
222   NULL     B    0xE20F40   /222/        2             2             1
55    222      B2   0xE20F747C /222/55/     4             1             2
66    222      B1   0xE20F7654 /222/66/     5             2             2
*/

答案 1 :(得分:1)

这将允许您在不跳过箍的情况下正确排序,随时更改数据类型,只需记住DisplayOrder + ' - ' + CatID中的变量int是否会评估为1+0+1=2而不是'1 - 1' CREATE TABLE #table ( CatID INT PRIMARY KEY ,ParentID INT NULL ,NAME VARCHAR(50) ,DisplayOrder INT ); INSERT #table VALUES (1,NULL,'A',1) ,(2,1,'A1',1) ,(3,1,'A2',2) ,(4,NULL,'B',2); SELECT * FROM ( SELECT CatID ,ParentID ,Convert(VARCHAR(50), DisplayOrder) + ' - ' + Convert(VARCHAR(50), CatID) AS LevelOne ,NULL AS LevelTwo FROM #table WHERE ParentID IS NULL UNION ALL SELECT c.CatID ,c.ParentID ,Convert(VARCHAR(50), d.DisplayOrder) + ' - ' + Convert(VARCHAR(50), d.CatID) AS LevelOne ,c.DisplayOrder AS LevelTwo FROM #table c INNER JOIN #table d ON c.ParentID = d.CatID WHERE c.ParentID IS NOT NULL ) AS Cats ORDER BY Cats.LevelOne ,Cats.LevelTwo; DROP TABLE #table

CatID   ParentID    LevelOne    LevelTwo
1       NULL        1 - 1       NULL
2       1           1 - 1       1
3       1           1 - 1       2
4       NULL        2 - 4       NULL

这导致:

{{1}}