2009-02-05 22 views
14

I wprowadziły połączonej listy jako samodzielny przedstawieniu tabeli bazy danych:Jak sortować połączoną listę w sql?

CREATE TABLE LinkedList(
    Id bigint NOT NULL, 
    ParentId bigint NULL, 
    SomeData nvarchar(50) NOT NULL) 

gdzie id jest kluczem podstawowym i parentId jest identyfikator poprzedniego węzła na liście. Pierwszy węzeł ma ParentId = NULL.

Teraz chcę WYBRAĆ z tabeli, sortując wiersze w takiej samej kolejności, w jakiej powinny się pojawić, jako węzły na liście.

Np .: jeśli tabela zawiera wiersze

Id  ParentId SomeData 
24971 NULL  0 
38324 24971  1 
60088 60089  3 
60089 38324  2 
61039 61497  5 
61497 60088  4 
109397 109831 7 
109831 61039  6 

Następnie sortowania go, stosując kryteria, powinno skutkować:

Id  ParentId SomeData 
24971 NULL  0 
38324 24971  1 
60089 38324  2 
60088 60089  3 
61497 60088  4 
61039 61497  5 
109831 61039  6 
109397 109831 7 

Miałeś użyć SomeData Columa jako kontrola, więc proszę nie oszukiwać robiąc ZAMÓW przez SomeData :-)

+0

Twój komentarz nie oszukuje: byłby lepszy, gdybyś wybrał przykładowe dane, które nie uzyskałyby poprawnego wyniku podczas samodzielnego sortowania. W ten sposób "oszustwo" nie byłoby możliwe. – AnthonyWJones

+1

Hmm ... Nie sądzę, że zrozumiałeś moją intencję podczas dodawania tej kolumny. Po prostu chciałem ułatwić życie ludziom, którzy mogą znaleźć odpowiedź. Nazwij to "narzędziem do testowania". –

Odpowiedz

9

W Oracle:

SELECT Id, ParentId, SomeData 
FROM (
    SELECT ll.*, level AS lvl 
    FROM LinkedList ll 
    START WITH 
    ParentID IS NULL 
    CONNECT BY 
    ParentId = PRIOR Id 
) 
ORDER BY 
    lvl 

P. S. To zły praktyką jest stosowanie NULL jak ParentID, gdyż nie można przeszukiwać według indeksów. Zamiast tego wstaw zastępczy root o identyfikatorze 0 lub -1 i użyj START WITH ParentID = 0.

+0

To bardzo dobra odpowiedź. Jak zrobić to samo w SQLServer 2005? –

+0

+1, ale nie dla komentarza anty-NULL: użycie 0 lub -1 wyklucza posiadanie obcego klucza w celu wymuszenia integralności. –

+0

Zawsze możesz zachować zastępczy root i przechowywać go w bazie danych. Pełne skanowanie pierwszego wpisu będzie zbyt wolne, jeśli tabela zawiera wiele rekordów. – Quassnoi

10

Znalazłem rozwiązanie dla SQLServer, ale wygląda na duży i znacznie mniej elegancki niż Quassnoi za

WITH SortedList (Id, ParentId, SomeData, Level) 
AS 
(
    SELECT Id, ParentId, SomeData, 0 as Level 
    FROM LinkedList 
    WHERE ParentId IS NULL 
    UNION ALL 
    SELECT ll.Id, ll.ParentId, ll.SomeData, Level+1 as Level 
    FROM LinkedList ll 
    INNER JOIN SortedList as s 
     ON ll.ParentId = s.Id 
) 

SELECT Id, ParentId, SomeData 
    FROM SortedList 
ORDER BY Level 
+1

Właściwie nazwa SortedList jest nieco myląca - to nie jest właściwie posortowane - po prostu rekursywne ... musisz ZLECZYĆ PRZEZ WYBRANY SELEKT (zobacz moją odpowiedź) –

+0

Dość, dodałem kolumnę Level i ORDER BY w ostatnim SELECT. –

+0

Czy można zastosować podobne podejście do SQLite? – Michael

5

(edit: d'oh Chociaż byłem debugowania znalazłeś je też!)

W SQL Serwer:

;WITH cte (Id, ParentId, SomeData, [Level]) AS (
    SELECT Id, ParentId, SomeData, 0 
    FROM LinkedList 
    WHERE ParentId IS NULL 
    UNION ALL 
    SELECT ll.Id, ll.ParentId, ll.SomeData, cte.[Level] + 1 
    FROM LinkedList ll 
    INNER JOIN cte ON ll.ParentID = cte.ID 
) 
SELECT * FROM cte 
ORDER BY [Level] 
+0

ah - błędne odczytanie pytania; Myślałem, że chcesz zamówić go przez SomeData; jeśli chcesz to w połączonej kolejności, to [Poziom] jest drogą do przejścia –

+1

Dlaczego półkolonia na początku? – Greg

+3

@Greg, ponieważ CTE są wybredne; prawie zawsze kończy się to wymaganiem prowadzenia; więc równie dobrze mogę to dodać. Dodaj * cokolwiek * powyżej przykładu i zaczyna bić –