2010-04-26 27 views
8

Próbuję wprowadzić hierarchyID w tabeli (dbo. [Message]) zawierający około 50 000 wierszy (znacznie wzrośnie w przyszłości). Uzyskanie około 25 wyników zajmuje jednak 30-40 sekund.Pytanie o SQL Server HierarchyID głęboka pierwsza wydajność

Węzeł główny jest wypełniaczem w celu zapewnienia unikalności, dlatego każdy kolejny wiersz jest dzieckiem tego wiersza manekina.

Muszę być w stanie przejść przez głębię stołu-pierwszy i uczyniłem kolumnę hierarchyID (dbo. [Wiadomość] .MessageID) kluczem kluczowym dla grupowania, również dodano obliczoną małą tablicę (dbo. [Wiadomość] .Hierarchia), który przechowuje poziom węzła.

Użycie: Aplikacja .Net przechodzi przez wartość hierarchyID do bazy danych i chcę mieć możliwość pobrania wszystkich (jeśli istnieją) dzieci i rodziców tego węzła (oprócz root, ponieważ jest to wypełniacz).

Uproszczona wersja zapytania używam:

@MessageID hierarchyID /* passed in from application */ 

SELECT 
m.MessageID, m.MessageComment 

FROM 
dbo.[Message] as m 

WHERE 
m.Messageid.IsDescendantOf(@MessageID.GetAncestor((@MessageID.GetLevel()-1))) = 1 

ORDER BY 
m.MessageID 

Z tego co rozumiem, indeks powinien być wykrywane automatycznie bez śladu.

Z forów wyszukiwania Widziałem ludzi wykorzystujących wskazówki do indeksów w przypadku pierwszych indeksów, ale nie zauważyłem tej aplikacji w pierwszych, głębokich sytuacjach. Czy byłoby to odpowiednie podejście do mojego scenariusza?

Spędziłem kilka ostatnich dni próbując znaleźć rozwiązanie tego problemu, ale bezskutecznie. Byłbym bardzo wdzięczny za wszelką pomoc, a ponieważ jest to mój pierwszy wpis, przepraszam z góry, jeśli byłoby to uznane za pytanie "noobish", przeczytałem dokumentację MS i przeszukałem niezliczone fora, ale nie natknąłem się na zwięzły opis konkretnej kwestii.

+0

Przy okazji, zapytanie, które masz? Jak napisano, zawsze wybiera się WSZYSTKIE węzły w całej tabeli. '@ MessageID.GetAncestor (@ MessageID.GetLevel() - 1)' przenosi wszystko aż do katalogu głównego, a następnie wybierasz wszystko, co jest potomkiem, czyli ... wszystko. Dlatego jest tak powolny. – Aaronaught

+0

Aby wyjaśnić: moja sytuacja wymaga użycia pierwszego zindeksowania, przepraszam za zamieszanie (chodzi mi o szerokość - na samym końcu tylko po to, aby podać przykład miejsca, w którym ludzie sugerowali użycie wskazówek indeksowych). – ObjectiveCat

Odpowiedz

2

Znaleziono obejście tutaj: http://connect.microsoft.com/SQLServer/feedback/details/532406/performance-issue-with-hierarchyid-fun-isdescendantof-in-where-clause#

Wystarczy przypomnieć, że zacząłem z heirarchyID przeszedł z aplikacji, a moim celem jest, aby odzyskać wszelkie krewnych tej wartości (zarówno przodków i potomków).

W moim konkretnym przykładzie, musiałem dodać następujące oświadczenia przed stwierdzeniem SELECT:

declare @topNode hierarchyid = (select @messageID.GetAncestor((@messageID.GetLevel()-1))) 
declare @topNodeParent hierarchyid = (select @topNode.GetAncestor(1)) 
declare @leftNode hierarchyid= (select @topNodeParent.GetDescendant (null, @topNode)) 
declare @rightNode hierarchyid= (select @topNodeParent.GetDescendant (@topNode, null)) 

Klauzula WHERE została zmieniona na:

messageid.IsDescendantOf(@topNode)=1 AND (messageid > @leftNode) AND (messageid < @rightNode) 

wysyłania żądania wzrost wydajności jest bardzo istotna :

Za każdy podany wynik czas wyszukiwania wynosi obecnie średnio 20ms (od 120 do 420).

Podczas sprawdzania 25 wartości poprzednio trwało 25 - 35 sekund, aby zwrócić wszystkie powiązane węzły (w niektórych przypadkach każda wartość miała wielu krewnych, w niektórych nie było żadnych). Teraz zajmuje to tylko 2 sekundy.

Dziękuję wszystkim, którzy przyczynili się do tego problemu na tej stronie i na innych.

8

Nie jest do końca jasne, czy próbujesz zoptymalizować pod kątem pierwszego lub największego wyszukiwania; pytanie sugeruje głębię, ale komentarze na końcu są o szerokości - pierwsze.

Masz wszystkie indeksy potrzebne do głębokości-pierwszej (wystarczy zindeksować kolumnę hierarchyid). Dla wszerz, to nie wystarczy, aby tworzyć wyliczonej level kolumnę, trzeba indeksu to zbyt:

ALTER TABLE Message 
ADD [Level] AS MessageID.GetLevel() 

CREATE INDEX IX_Message_BreadthFirst 
ON Message (Level, MessageID) 
INCLUDE (...) 

(Zauważ, że dla indeksów nieklastrowych będziesz najprawdopodobniej potrzebował INCLUDE - inaczej SQL Server może zamiast tego wykonać skanowanie w klastrze indeksu.)

Teraz, jeśli próbujesz znaleźć wszystkich przodków węzła, chcesz wziąć nieco inną hals. Możesz uczynić te wyszukiwania błyskawicznie, ponieważ - i oto co jest fajne o hierarchyid - każdy węzeł "zawiera" wszystkich swoich przodków.

używam funkcji CLR, aby to tak szybko, jak to możliwe, ale można to zrobić z rekurencyjnej CTE:

CREATE FUNCTION dbo.GetAncestors 
(
    @h hierarchyid 
) 
RETURNS TABLE 
AS RETURN 
WITH Hierarchy_CTE AS 
(
    SELECT @h AS id 

    UNION ALL 

    SELECT h.id.GetAncestor(1) 
    FROM Hierarchy_CTE h 
    WHERE h.id <> hierarchyid::GetRoot() 
) 
SELECT id FROM Hierarchy_CTE 

Teraz, aby uzyskać wszystkie przodków i potomków, używać go tak:

DECLARE @MessageID hierarchyID /* passed in from application */ 

SELECT m.MessageID, m.MessageComment 
FROM Message as m 
WHERE m.MessageId.IsDescendantOf(@MessageID) = 1 
OR m.MessageId IN (SELECT id FROM dbo.GetAncestors(@MessageID.GetAncestor(1))) 
ORDER BY m.MessageID 

Wypróbuj - to powinno rozwiązać problemy z wydajnością.

+0

Przepraszamy za zamieszanie, głębia - pierwsza to naprawdę to, o co lecę! Wielkie dzięki za sugestię, spróbuję od razu. – ObjectiveCat

+0

tylko w celach testowych, usunąłem @ MessageID.GetAncestor całkowicie pozostawiając tylko: m.MessageId.IsDescendantOf (@MessageID) = 1 w klauzuli WHERE, a kiedy wpadłem że proc, czas wyszukiwania jest wciąż między 150 do 420ms za wynik, co jest bardzo wolne w mojej aplikacji. Wydajność jest priorytetem i nie jestem całkowicie zaznajomiony z językiem CLR, ale bardzo chciałbym się dowiedzieć, jak ją wdrożyć, jeśli zapewniałoby to najlepszą wydajność. Wszelkie sugestie na temat, od czego zacząć? – ObjectiveCat

+0

@AndalusianCat: Wersja CLR jest przeznaczona dla zapytania przodka. Jeśli uważasz, że jest wolny, używając 'IsDescendantOf', opublikuj rzeczywiste zapytanie, schemat tabeli (w tym indeksy) i plan wykonania. Zapytania "hierarchyid" są zwykle znacznie szybsze. – Aaronaught