2008-10-10 17 views
8

Mam tabelę na serwerze SQL, który ma normalną strukturę drzewa Item_ID, Item_ParentID. Załóżmy, że chcę powtórzyć i uzyskać wszystkie DZIECI określonego Item_ID (na dowolnym poziomie).Czy rekursja jest dobra w SQL Server?

Rekursja wydaje się być intuicyjnym kandydatem do tego problemu i mogę napisać funkcję serwera SQL, aby to zrobić.

Czy wpłynie to na wydajność, jeśli mój stół ma wiele rekordów? Jak uniknąć rekursji i po prostu zapytać o tabelę? Proszę o jakieś sugestie?

Odpowiedz

4

Dzięki nowej MS SQL 2005 można użyć WITH słowa kluczowego

Wyjazd this question a szczególnie this answer.

W Oracle można użyć słowa kluczowego CONNECT BY do generowania zapytań hierarchicznych (syntax).

AFAIK z MySQL będziesz musiał użyć rekursji.

Ewentualnie zawsze można zbudować tabelę pamięci podręcznej dla rekordów nadrzędnej> relacje dzieci

0

Być może trochę więcej szczegółów jest w porządku.

Jeśli opisujesz relację mistrz-szczegół, to czy prosty JOIN nie dostanie tego, czego potrzebujesz?

Jak w:

SELECT 
    SOME_FIELDS 
FROM 
    MASTER_TABLE MT 
,CHILD_TABLE CT 
WHERE CT.PARENT_ID = MT.ITEM_ID 
1

używasz SQL 2005?

Jeśli tak, możesz użyć do tego celu Common Common Expressions. Coś wzdłuż tych linii:

; 
with CTE (Some, Columns, ItemId, ParentId) as 
(
    select Some, Columns, ItemId, ParentId 
    from myTable 
    where ItemId = @itemID 
    union all 
    select a.Some, a.Columns, a.ItemId, a.ParentId 
    from myTable as a 
    inner join CTE as b on a.ParentId = b.ItemId 
    where a.ItemId <> b.ItemId 
) 
select * from CTE 
0

Nie powinno być potrzeby rekursji dla dzieci - szukasz tylko na poziomie bezpośrednio poniżej (tj select * from T where ParentId = @parent) - trzeba tylko rekursji dla wszystkich potomków.

W SQL2005 można uzyskać potomków z:

with AllDescendants (ItemId, ItemText) as (
    select t.ItemId, t.ItemText 
     from [TableName] t 
    where t.ItemId = @ancestorId 
    union 
    select sub.ItemId, sub.ItemText 
     from [TableName] sub 
      inner join [TableName] tree 
      on tree.ItemId = sub.ParentItemId 
) 
+0

Myślę, że jego problemem jest to, że chce „W szczególny poziom ". Jeśli nie przechowujesz numeru poziomu, skąd wiesz, co jest na danym poziomie, bez przechodzenia na poziom root = 1, dzieci root = poziom 2, dzieci dzieci = poziom 3, itd. Nie jest potrzebna rekursja. . Ale może być wielu rodziców. – Cervo

1

Problemem będzie zmierzyć z rekursji i wydajność jest to, ile razy będzie musiał wrócić recurse wyniki. Każde wywołanie rekurencyjne jest kolejnym oddzielnym wywołaniem, które będzie musiało zostać połączone w całość wyników.

W SQL 2K5 można korzystać ze wspólnego wyrażenia stół do obsługi tej rekurencji:

WITH Managers AS 
( 
--initialization 
SELECT EmployeeID, LastName, ReportsTo 
FROM Employees 
WHERE ReportsTo IS NULL 
UNION ALL 
--recursive execution 
SELECT e.employeeID,e.LastName, e.ReportsTo 
FROM Employees e INNER JOIN Managers m 
ON e.ReportsTo = m.employeeID 
) 
SELECT * FROM Managers 

lub innym rozwiązaniem jest spłaszczenie hierarchii w innej tabeli

Employee_Managers
ManagerId (PK , FK do tabeli pracownika)
EmployeeId (PK, FK do tabeli pracownika)

Wszystkie rodzic dziecko statki kadrowe będą przechowywane w tabeli, więc jeśli Menedżer 1 zarządza Manager 2 zarządza pracownikowi 3, tabela będzie wyglądać następująco:

ManagerId EmployeeId 
1   2 
1   3 
2   1 

Pozwala to hierarchia być łatwo zapytał:

select * from employee_managers em 
inner join employee e on e.employeeid = em.employeeid and em.managerid = 42 

który powróci wszystkich pracowników, które mają menedżera 42. Plusem będzie większa wydajność, ale minusem będzie utrzymanie hierarchii

2

jako ogólną odpowiedź, że jest możliwe, aby zrobić kilka dość wyrafinowane rzeczy w SQL Serwer, który nie rmally potrzebuje rekurencji, po prostu używając algorytmu iteracyjnego. Udało mi się zrobić parser XHTML w języku Transact SQL, który zadziałał zaskakująco dobrze. Napisany przeze mnie kod został wykonany w procedurze przechowywanej. Nie jest elegancki, jest raczej jak oglądanie bawoła wykonującego Balet. ale działa.

+0

Kiedyś napisałem odtwarzacz wideo w języku Transact-SQL! :-P –

+0

Możesz, ale na pewno byłoby o wiele łatwiej napisać parser XHTML w Pythonie/Ruby/C#/Java/Perl/LISP/etc. A wydajność prawdopodobnie byłaby lepsza ... – Cervo

+0

Bez szczerości. To tu! http://www.simple-talk.com/sql/t-sql-programming/getting-html-data-workbench/ –

1

Joe Celko ma book (< - link do Amazon) w szczególności na strukturach drzew w bazach danych SQL. Chociaż potrzebujesz rekurencji dla swojego modelu i na pewno istnieje potencjalny problem z wydajnością, istnieją alternatywne sposoby modelowania struktury drzewa w zależności od tego, co obejmuje konkretny problem, co może zapobiec rekurencji i zapewnić lepszą wydajność.

0

Nie trzeba rekursji w ogóle .... Uwaga, zmieniłem kolumny do ItemID i ItemParentID dla łatwości pisania ...

 
DECLARE @intLevel INT 
SET @intLevel = 1

INSERT INTO TempTable(ItemID, ItemParentID, Level) SELECT ItemID, ItemParentID, @intLevel WHERE ItemParentID IS NULL

WHILE @intLevel < @TargetLevel BEGIN SET @intLevel = @intLevel + 1 INSERT INTO TempTable(ItemID, ItemParentID, Level) SELECt ItemID, ItemParentID, @intLevel WHERE ItemParentID IN (SELECT ItemID FROM TempTable WHERE Level = @intLevel-1) -- If no rows are inserted then there are no children IF @@ROWCOUNT = 0 BREAK END

SELECt ItemID FROM TempTable WHERE Level = @TargetLevel