SQL Query使用嵌套数据

时间:2016-04-14 20:47:15

标签: sql sql-server

我有这种结构的观点:

EntryId | EntryName | ParentEntryId | Depth | DatePosted

我想要做的是编写一个SQL查询,它将带有Depth = 0的前两个条目以及第一个后代(基于ParentEntryId)。下面,我提供了一个示例输出。

EntryId | EntryName | ParentEntryId | Depth | DatePosted | ChildCount
1       | a         | NULL          | 0     | 1/12/2012  | 2
4       | b         | 1             | 1     | 1/14/2012  | 5
13      | c         | 1             | 1     | 1/15/2012  | 3
3       | d         | NULL          | 0     | 1/11/2012  | 1
12      | e         | 3             | 1     | 1/14/2012  | 0

我知道我可以像这样轻松地输入深度= 0的条目:

SELECT TOP 10 FROM Entries WHERE Depth=0 ORDER BY DatePosted DESC

但是,我不知道如何带来相关的子条目。例如,对于Id = 1的主条目,我想带上ParentEntryId = 1的条目(第一个后代)。我还需要带来这些第一个后代的子条目的计数。有什么想法吗?

4 个答案:

答案 0 :(得分:2)

根据您更新的问题,以下查询将产生完美的结果

SELECT 
   EntryId , EntryName , ParentEntryId , Depth , DatePosted, ChildCount 
  FROM 
    (   
      SELECT 
        TOP 10 
          E1.EntryId , E1.EntryName , E1.ParentEntryId , E1.Depth , E1.DatePosted, 
           (
             SELECT 
               COUNT(1) 
             FROM Entries E2 
              WHERE E2.ParentEntryID =E1.EntryID
             ) as ChildCount 
      FROM Entries E1 
       WHERE E1.Depth=0

      UNION 

      SELECT 
        E1.EntryId , E1.EntryName , E1.ParentEntryId , E1.Depth , E1.DatePosted, 
        (
            SELECT 
            COUNT(1) 
            FROM Entries E3 
            WHERE E3.ParentEntryID =E1.EntryID
            ) as ChildCount 
       FROM Entries E1 
        LEFT JOIN Entries E2 ON E1.ParentEntryID= E2.EntryID AND E2.Depth=0
    )
    ORDER BY ParentEntryID , Depth ASC, DatePosted

答案 1 :(得分:1)

您也可以使用递归cte进行此操作..如果您在大型记录集上使用此功能,则应确保性能符合您的标准

;WITH cte AS (
    SELECT  [EntryId],
            [EntryName],
            [ParentEntryId],
            [Depth],
            [DatePosted],
            [EntryId] [Root],
            ROW_NUMBER() OVER (ORDER BY DatePosted DESC) [Rn],
            CAST(EntryId AS VARCHAR(MAX)) [Path]
    FROM    Entries
    WHERE   [Depth] = 0
    UNION ALL
    SELECT  e.[EntryId],
            e.[EntryName],
            e.[ParentEntryId],
            e.[Depth],
            e.[DatePosted],
            [Root],
            Rn,
            [Path] + ',' + CAST(e.EntryId AS VARCHAR(MAX))
    FROM    Entries e
            JOIN cte ON cte.EntryID = e.ParentEntryId 
)
SELECT  [EntryId],
        [EntryName],
        [ParentEntryId],
        [Depth],
        [DatePosted],
        ChildCount
FROM    cte c1
        OUTER APPLY (SELECT COUNT (*) - 1 AS ChildCount 
                     FROM cte c2 
                     WHERE c2.[Path] LIKE c1.[Path] + '%'
                    ) oa
WHERE   Rn <= 2  -- only gets the first 2 records with depth = 0
        AND Depth <= 1  -- limit to only top level child records
ORDER BY [Root],
        [ParentEntryID]

答案 2 :(得分:1)

如果没有任何输入数据,很难给出准确的答案。但是,我认为这就是你要找的东西。

我使用了一个简单的数据集进行测试。我试图对脚本的布局进行分组,以便您可以根据ParentEntryID轻松查看子项数:

-- Create a table.
DROP TABLE Entries
CREATE TABLE Entries
(
    EntryID         INT,
    EntryName       VARCHAR(20),
    ParentEntryID   INT,
    Depth           INT, 
    DatePosted      DATE     
);

-- Populate the table
INSERT INTO Entries VALUES 
 (1,  'A', null, null, CURRENT_TIMESTAMP)
,(73, 'C', 1, 0, CURRENT_TIMESTAMP)

,(16, 'B', 73, 1, CURRENT_TIMESTAMP)
,(85, 'G', 73, 1, DATEADD(DAY, 1, CURRENT_TIMESTAMP))
,(74, 'D', 73, 1, CURRENT_TIMESTAMP)

,(75, 'E', 74, 2, CURRENT_TIMESTAMP)
,(76, 'F', 74, 2, CURRENT_TIMESTAMP)

,(86, 'H', 85, 3, DATEADD(DAY, 2, CURRENT_TIMESTAMP))
,(87, 'I', 85, 3, DATEADD(DAY, 2, CURRENT_TIMESTAMP))

,(88, 'J', 86, 4, DATEADD(DAY, 3, CURRENT_TIMESTAMP))

,(89, 'K', 88, 5, CURRENT_TIMESTAMP)
,(90, 'L', 88, 5, CURRENT_TIMESTAMP)
,(91, 'M', 88, 5, CURRENT_TIMESTAMP)
,(92, 'N', 88, 5, CURRENT_TIMESTAMP);

然后您可以使用递归公用表表达式。我已评论TOP 2WHERE Depth = 0以提供更多结果。 (我认为这使得基于少量测试数据更容易理解。)您可以将这些替换为您的要求。

;WITH MyEntries (EntryID, ParentEntryID, EntryName, Depth, DatePosted)
AS
(
    -- Anchor 
    SELECT  EntryID, ParentEntryID, EntryName, Depth, DatePosted 
    FROM    Entries
    --WHERE   Depth = 0
    UNION ALL
    -- Recursive
    SELECT  Recurs.EntryID, Recurs.ParentEntryID, Recurs.EntryName, Recurs.Depth, Recurs.DatePosted
    FROM    Entries AS Recurs     
            INNER JOIN MyEntries AS Anchor
                ON Recurs.EntryID = Anchor.ParentEntryID    
    --WHERE Recurs.Depth = 0    
)
SELECT  DISTINCT 
        --TOP 2                 
        ME.EntryID
        ,ME.ParentEntryID
        ,ME.EntryName
        ,ME.Depth
        ,ME.DatePosted
        ,COALESCE(VT.ChildCOunt, 0) AS 'ChildCount'
FROM    MyEntries AS ME
LEFT JOIN   (SELECT  ParentEntryID, COUNT(1) AS 'ChildCount'
            FROM Entries
            GROUP BY ParentEntryID ) AS VT
        ON ME.EntryID = VT.ParentEntryID            
ORDER BY        
        ME.DatePosted;

我不确定这是最有效的方式,但似乎有效。

答案 3 :(得分:0)

你有前十名但没有订单,所以你的逻辑从一开始就有缺陷。您也没有列出任何列。作为一个纯粹的猜测,因为你没有提供太多的细节,我在想这样的事情。

select [Columns]
from Entries
where ParentEntryID in 
(
    select top 10 EntryID
    from Entries
    where Depth = 10
    order by SomeColumn
)

或许你需要一个递归的cte来获得每棵树下的整棵树?