2010-04-26 7 views
8

Ich versuche, hierarchyID in einer Tabelle (dbo. [Nachricht]) zu implementieren, die etwa 50.000 Zeilen enthält (wird in Zukunft wesentlich wachsen). Es dauert jedoch 30-40 Sekunden, um ungefähr 25 Ergebnisse zu erhalten.Frage zu SQL Server HierarchyID Tiefe erste Leistung

Der Wurzelknoten ist ein Füller, um Eindeutigkeit zu gewährleisten, daher ist jede nachfolgende Zeile ein Kind dieser Dummy-Zeile.

Ich muss in der Lage sein, die Tabelle depth-first durchlaufen und haben die Hierarchie-ID-Spalte (dbo. [Nachricht] .MessageID) der Clustering-Primärschlüssel, auch eine berechnete smallint (dbo. [Nachricht] .Hierarchie) welche die Ebene des Knotens speichert.

Verwendung: Eine .Net-Anwendung durchläuft einen hierarchyID-Wert in der Datenbank und ich möchte in der Lage sein, alle (falls vorhanden) untergeordneten UND-Eltern dieses Knotens (neben der Wurzel, wie es ist Füllstoff) abrufen.

Eine vereinfachte Version der Abfrage ich verwende:

@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 

Von dem, was ich verstehe, sollte der Index ohne einen Hauch automatisch erkannt werden.

Aus der Suche in Foren habe ich Leute gesehen, die Index-Hinweise verwenden, wenn sie mit breast-first-Indizes umgehen, aber diese Anwendung nicht in der ersten Tiefe beobachtet haben. Wäre das ein relevanter Ansatz für mein Szenario?

Ich habe in den letzten paar Tagen versucht, eine Lösung für dieses Problem zu finden, aber ohne Erfolg. Ich wäre sehr dankbar für jede Hilfe, und da dies meine erste Post ist, entschuldige ich mich im Voraus, wenn dies eine 'noobish' Frage wäre, habe ich die MS-Dokumentation gelesen und unzählige Foren durchforstet, bin aber nicht auf eine kurze Beschreibung gestoßen des spezifischen Problems.

+0

Übrigens, die Abfrage, die Sie haben? Wie geschrieben, wird es immer ALLE Knoten in der gesamten Tabelle auswählen. Der '@ MessageID.GetAncestor (@ MessageID.GetLevel() - 1)' nimmt es bis zur Wurzel, und dann wählen Sie alles aus, was ein Nachkomme ist, was ... alles ist. Deshalb ist es so langsam. – Aaronaught

+0

Nur um zu verdeutlichen: meine Situation erfordert die Verwendung von Tiefen-Indizierung, sorry für Verwirrung (ich bezog sich auf Breite zuerst am Ende, um ein Beispiel zu geben, wo Leute vorgeschlagen haben, Index-Hinweise zu verwenden) – ObjectiveCat

Antwort

2

gefunden Abhilfe hier: http://connect.microsoft.com/SQLServer/feedback/details/532406/performance-issue-with-hierarchyid-fun-isdescendantof-in-where-clause#

nur daran erinnern, dass ich mit einem heirarchyID bestanden in der Anwendung gestartet und mein Ziel ist es, alle und alle Verwandten dieser Wert (beide Vorfahren und Nachkommen) abgerufen werden.

In meinem speziellen Beispiel, ich hatte die folgenden Erklärungen vor der SELECT Anweisung hinzuzufügen:

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)) 

Die WHERE Klausel geändert wurde:

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

Die anfragende Leistungssteigerung ist sehr wichtig :

Für jedes eingegebene Ergebnis beträgt die Suchzeit nun durchschnittlich 20ms (von 120 bis 420).

Bei der Abfrage von 25 Werten dauerte es zuvor 25 - 35 Sekunden, um alle zugehörigen Knoten zurückzugeben (in einigen Fällen hatte jeder Wert viele Verwandte, in einigen gab es keine). Es dauert jetzt nur 2 Sekunden.

Vielen Dank an alle, die auf dieser Seite und auf anderen zu diesem Thema beigetragen haben.

8

Es ist nicht ganz klar, ob Sie versuchen, die Suche nach der Tiefe oder der Breite zuerst zu optimieren. Die Frage schlägt Tiefe zuerst, aber die Kommentare am Ende sind über Breite zuerst.

Sie haben alle Indizes, die Sie für die Tiefe benötigen (indexieren Sie einfach die Spalte hierarchyid). Für Breiten erste ist, ist es nicht genug, nur zu erstellen die berechnete level Spalte, haben Sie indizieren auch sie:

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

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

(Beachten Sie, dass für nicht gruppierte Indizes Sie wahrscheinlich die INCLUDE brauchen - sonst , SQL Server kann stattdessen eine gruppierte Indexsuche durchführen.)

Jetzt, wenn Sie versuchen, alle Vorfahren eines Knotens zu finden, möchten Sie eine etwas andere Wende nehmen. Sie können diese Suchen blitzschnell durchführen, denn - und hier ist, was ist cool an hierarchyid - jeder Knoten "enthält" bereits alle seine Vorfahren.

Ich verwende eine CLR-Funktion, um diese so schnell wie möglich zu machen, aber man kann es mit einem rekursiven CTE tun:

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 

nun alle Vorfahren und Nachkommen zu erhalten, ist es wie folgt verwenden:

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 

Probieren Sie es aus - das sollte Ihre Leistungsprobleme lösen.

+0

Entschuldigung für die Verwirrung, Tiefe-zuerst ist in der Tat, was ich danach bin! Vielen Dank für den Vorschlag, ich werde es sofort versuchen. – ObjectiveCat

+0

nur zu Testzwecken, ich habe @ MessageID.GetAncestor insgesamt verlassen nur entfernt: m.MessageId.IsDescendantOf (@MessageID) = 1 in der WHERE-Klausel, und wenn ich das proc lief, Zeit zu suchen, ist immer noch zwischen 150 zu 420ms pro Ergebnis, das ist sehr langsam für meine Anwendung. Leistung ist eine Priorität, und ich bin CLR völlig unbekannt, aber ich würde wirklich gerne lernen, wie man es implementiert, wenn das die beste Leistung liefern würde. Irgendwelche Vorschläge, wo man anfangen soll? – ObjectiveCat

+0

@AndalusianCat: Die CLR-Version ist für die Ahnenfrage. Wenn Sie es langsam finden nur mit 'IsDescendantOf', posten Sie bitte eine tatsächliche Abfrage, ein Tabellenschema (einschließlich Indizes) und den Ausführungsplan. 'hierarchyid' Anfragen sind typischerweise viel schneller als das. – Aaronaught