2013-08-07 15 views
27

Muszę uporządkowanej hierarchii drzewa, w specyficzny sposób. W tabeli w pytaniu wygląda trochę tak (wszystkie pola ID są uniqueidentifiers, mam uproszczone dane dla dobra przykład):CTE Recursion dostać drzewo hierarchii

EstimateItemID EstimateID ParentEstimateItemID  ItemType 
-------------- ---------- --------------------  -------- 
     1    A    NULL    product 
     2    A     1    product 
     3    A     2    service 
     4    A    NULL    product 
     5    A     4    product 
     6    A     5    service 
     7    A     1    service 
     8    A     4    product

graficzny widok struktury drzewa (* „usługa” oznacza):

 
      A 
     ___/ \___ 
    /  \ 
    1   4 
/\  /\ 
    2 7*  5 8 
/  /
3*   6* 

Korzystanie z tej kwerendy, mogę hierarchię (udawać „A” jest uniqueidentifier, wiem, że nie jest w prawdziwym życiu):

DECLARE @EstimateID uniqueidentifier 
SELECT @EstimateID = 'A' 

;WITH temp as(
    SELECT * FROM EstimateItem 
    WHERE EstimateID = @EstimateID 

    UNION ALL 

    SELECT ei.* FROM EstimateItem ei 
    INNER JOIN temp x ON ei.ParentEstimateItemID = x.EstimateItemID 
) 

SELECT * FROM temp 

to daje mi dzieci EstimateID "A", ale w kolejność, w jakiej pojawia się w tabeli. tj:

EstimateItemID 
-------------- 
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8

Niestety, co potrzebne jest uporządkowana hierarchia ze zbioru wynikowego, że stosuje następujące ograniczenia:

 
1. each branch must be grouped 
2. records with ItemType 'product' and parent are the top node 
3. records with ItemType 'product' and non-NULL parent grouped after top node 
4. records with ItemType 'service' are bottom node of a branch 

Tak, kolejność, że muszę wyniki, w tym przykładzie, :

EstimateItemID 
-------------- 
     1 
     2 
     3 
     7 
     4 
     5 
     8 
     6 

Co muszę dodać do mojego zapytania, aby to osiągnąć?

Odpowiedz

53

Spróbuj tego:

;WITH items AS (
    SELECT EstimateItemID, ItemType 
    , 0 AS Level 
    , CAST(EstimateItemID AS VARCHAR(255)) AS Path 
    FROM EstimateItem 
    WHERE ParentEstimateItemID IS NULL AND EstimateID = @EstimateID 

    UNION ALL 

    SELECT i.EstimateItemID, i.ItemType 
    , Level + 1 
    , CAST(Path + '.' + CAST(i.EstimateItemID AS VARCHAR(255)) AS VARCHAR(255)) 
    FROM EstimateItem i 
    INNER JOIN items itms ON itms.EstimateItemID = i.ParentEstimateItemID 
) 

SELECT * FROM items ORDER BY Path 

Z Path - wiersze posortowana przez rodziców węzłów

Jeśli chcesz uporządkować childNodes autorem ItemType na każdym poziomie, niż można grać z Level i SUBSTRING z Path kolumnie. ...

Tutaj SQLFiddle z próbką danych

+0

Genialny. Jest to kilka lat, ale dziś okazało się przydatne. Jednak, wybaczając to, stwierdziłem, że przykład podany w oryginalnym poście jest dla mnie trudny do przetłumaczenia na bardziej powszechne rozwiązanie. Przekazałem więc Twój (świetny) pomysł, wykorzystując bardziej popularne dane, nazwę tabeli i pola, aby ułatwić innym śledzenie. – ptownbro

+0

Czy istnieje sposób na uporządkowanie według ItemType z poziomem 0 i hierarchia powinna pozostać taka, jaka jest? –

0

wierzę, że trzeba dodać następujące wynikach swojej CTE ...

  1. BranchID = jakiś identyfikator, który jednoznacznie identyfikuje oddział. Wybacz mi, że nie jestem bardziej konkretny, ale nie jestem pewien, co oznacza oddział dla twoich potrzeb. Twój przykład pokazuje drzewo binarne, w którym wszystkie gałęzie wracają do katalogu głównego.
  2. ItemTypeID gdzie (na przykład) 0 = Produkt i 1 = usługa.
  3. Rodzic = identyfikuje rodzica.

Jeśli istnieją na wyjściu, myślę, że powinieneś być w stanie wykorzystać wyniki z zapytania jako inny CTE lub jako klauzulę FROM w zapytaniu. Sortuj według BranchID, ItemTypeID, Parent.

+0

Korzeń oddział byłby identyfikowany przez zapis z NULL ParentEstimateItemID. Zatem wszystko pod "1" będzie gałęzią x, podczas gdy wszystko poniżej 4 będzie gałęzią y. Nie jestem niesamowicie wykwalifikowany w sql i uczę się CTE w locie, więc wybacz mi. Czy twoje punkty muszą być dodane w pierwszej instrukcji SELECT? – Woods8460

3

Jest to dodatek do świetnego pomysłu Fabio z góry. Tak jak powiedziałem w swojej odpowiedzi na jego pierwotny post. Ponownie opublikowałem jego pomysł, wykorzystując bardziej popularne dane, nazwę tabeli i pola, aby ułatwić innym śledzenie.

Dziękuję Fabio! Świetne imię przy okazji.

Pierwsza część danych do pracy z:

CREATE TABLE tblLocations (ID INT IDENTITY(1,1), Code VARCHAR(1), ParentID INT, Name VARCHAR(20)); 

INSERT INTO tblLocations (Code, ParentID, Name) VALUES 
('A', NULL, 'West'), 
('A', 1, 'WA'), 
('A', 2, 'Seattle'), 
('A', NULL, 'East'), 
('A', 4, 'NY'), 
('A', 5, 'New York'), 
('A', 1, 'NV'), 
('A', 7, 'Las Vegas'), 
('A', 2, 'Vancouver'), 
('A', 4, 'FL'), 
('A', 5, 'Buffalo'), 
('A', 1, 'CA'), 
('A', 10, 'Miami'), 
('A', 12, 'Los Angeles'), 
('A', 7, 'Reno'), 
('A', 12, 'San Francisco'), 
('A', 10, 'Orlando'), 
('A', 12, 'Sacramento'); 

teraz zapytania rekurencyjne:

-- Note: The 'Code' field isn't used, but you could add it to display more info. 
;WITH MyCTE AS (
    SELECT ID, Name, 0 AS TreeLevel, CAST(ID AS VARCHAR(255)) AS TreePath 
    FROM tblLocations T1 
    WHERE ParentID IS NULL 

    UNION ALL 

    SELECT T2.ID, T2.Name, TreeLevel + 1, CAST(TreePath + '.' + CAST(T2.ID AS VARCHAR(255)) AS VARCHAR(255)) AS TreePath 
    FROM tblLocations T2 
    INNER JOIN MyCTE itms ON itms.ID = T2.ParentID 
) 
-- Note: The 'replicate' function is not needed. Added it to give a visual of the results. 
SELECT ID, Replicate('.', TreeLevel * 4)+Name 'Name', TreeLevel, TreePath 
FROM MyCTE 
ORDER BY TreePath; 
+0

fajne rozwiązanie! Napisałem o SQLFiddle, http://sqlfiddle.com/#!6/5e70c/2/0 – MAbraham1