2016-04-14 5 views
2

У меня есть мнение с этой структурой:SQL запрос, чтобы прочитать определенное количество записей с вложенными данными

EntryId | EntryName | ParentEntryId | Depth | DatePosted 

То, что я хочу сделать, это написать SQL запрос, который будет приносить первые 2 записи с глубиной = 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. Мне также нужно привести счет дочерних элементов этих первых потомков. Есть идеи?

+0

Под сущностью вы имеете в виду всех детей этой записи или только следующего потомка? – DhruvJoshi

+0

@DhruvJoshi только потомок и подсчет субтитров потомка – renakre

+0

Вот отличное место для начала. http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/ –

ответ

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] 
+0

Это даст ChildCount как _total_ количество потомков, а не число _first_ потомков. Я понял вопрос ОП как желающего только числа детей, а не внуков и последующих потомков и т. Д., Но вопрос не совсем ясен. – Stainy

+0

спасибо за ответ, но UNION ALL возвращает повторяющиеся записи. – renakre

0

У вас есть 10 лучших, но нет порядка, так что ваша логика ошибочна с самого начала. У вас также нет столбцов. Как чистое предположение, так как вы не представили много деталей, я думаю об этом.

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

Возможно, вам нужен рекурсивный cte, чтобы получить все дерево под каждым из них?

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 
+0

, это имеет смысл, я постараюсь и буду держать вас в курсе, спасибо! – renakre

+0

Я думаю, что запрос после 'UNION' должен быть каким-то образом связан с запросом перед' UNION'. В противном случае он вернет некоторые записи, не зависящие от основных записей (depth = 0). – renakre

+0

@renakre обновлен! – DhruvJoshi

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 2 и WHERE 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; 

Я не уверен, что это самый эффективный способ, но он работает.