Indizierte Ansichten können in jeder Edition von SQL Server erstellt werden, aber es gibt eine Reihe von Verhaltensweisen, die Sie beachten sollten, wenn Sie sie optimal nutzen möchten.
Automatische Statistiken erfordern einen NOEXPAND-Hinweis
SQL Server kann automatisch Statistiken erstellen, um die Schätzung der Kardinalität und die kostenbasierte Entscheidungsfindung während der Abfrageoptimierung zu unterstützen. Diese Funktion funktioniert sowohl mit indizierten Ansichten als auch mit Basistabellen, aber nur, wenn die Ansicht explizit in der Abfrage und im NOEXPAND
benannt wird Hinweis angegeben. (Jedem Index einer Ansicht ist immer ein Statistikobjekt zugeordnet. Wir sprechen hier von der automatischen Generierung und Verwaltung von Statistiken, die keinem Index zugeordnet sind.)
Wenn Sie es gewohnt sind, mit Nicht-Enterprise-Editionen von SQL Server zu arbeiten, ist Ihnen dieses Verhalten möglicherweise noch nie aufgefallen. Niedrigere Editionen von SQL Server erfordern den NOEXPAND
Hinweis zum Erstellen eines Abfrageplans, der auf eine indizierte Ansicht zugreift. Wenn NOEXPAND
angegeben ist, werden automatische Statistiken für indizierte Ansichten genauso erstellt wie für gewöhnliche Tabellen.
Beispiel – Standard Edition mit NOEXPAND
Mithilfe von SQL Server 2012 Standard Edition und der Adventure Works-Beispieldatenbank erstellen wir zunächst eine Ansicht, die zwei Verkaufstabellen verknüpft und die Gesamtbestellmenge pro Kunde und Produkt berechnet:
CREATE VIEW dbo.CustomerOrders WITH SCHEMABINDING AS SELECT SOH.CustomerID, SOD.ProductID, OrderQty = SUM(SOD.OrderQty), NumRows = COUNT_BIG(*) FROM Sales.SalesOrderDetail AS SOD JOIN Sales.SalesOrderHeader AS SOH ON SOH.SalesOrderID = SOD.SalesOrderID GROUP BY SOH.CustomerID, SOD.ProductID;
Damit diese Ansicht Statistiken unterstützt, müssen wir sie materialisieren, indem wir einen eindeutigen gruppierten Index hinzufügen. Die Kombination aus Kunden- und Produkt-ID ist in der Ansicht (per Definition) garantiert eindeutig, daher verwenden wir diese als Schlüssel. Wir könnten die beiden Spalten in beiden Richtungen im Index angeben, aber vorausgesetzt, wir erwarten, dass mehr Abfragen nach Produkt filtern, machen wir die Produkt-ID zur führenden Spalte. Diese Aktion erstellt auch Indexstatistiken mit einem Histogramm, das aus Produkt-ID-Werten erstellt wird.
CREATE UNIQUE CLUSTERED INDEX cuq ON dbo.CustomerOrders (ProductID, CustomerID);
Wir werden nun gebeten, eine Abfrage zu schreiben, die die Gesamtzahl der Bestellungen pro Kunde für eine bestimmte Produktpalette anzeigt. Wir gehen davon aus, dass ein Ausführungsplan, der die indizierte Ansicht verwendet, eine effektive Strategie ist, da er eine Verknüpfung vermeidet und mit Daten arbeitet, die bereits teilweise aggregiert sind. Da wir SQL Server Standard Edition verwenden, müssen wir die Ansicht explizit angeben und ein NOEXPAND
verwenden Hinweis zum Erstellen eines Abfrageplans, der auf die indizierte Ansicht zugreift:
SELECT CO.CustomerID, SUM(CO.OrderQty) FROM dbo.CustomerOrders AS CO WITH (NOEXPAND) WHERE CO.ProductID BETWEEN 711 AND 718 GROUP BY CO.CustomerID;
Der erstellte Ausführungsplan zeigt eine Suche in der indizierten Ansicht, um Zeilen für die interessierenden Produkte zu finden, gefolgt von einer Aggregation, um die Gesamtmenge pro Kunde zu berechnen:
Die Planstrukturansicht von SQL Sentry Plan Explorer zeigt, dass die Kardinalitätsschätzung für die Suche in der indizierten Ansicht genau richtig und für das Ergebnis des Aggregats sehr gut ist:
Als Teil des Kompilierungs- und Optimierungsprozesses für diese Abfrage hat SQL Server ein zusätzliches Statistikobjekt für die Spalte „Kunden-ID“ der indizierten Ansicht erstellt. Diese Statistik wird erstellt, weil die erwartete Anzahl und Verteilung von Kunden-IDs wichtig sein könnten, beispielsweise bei der Auswahl einer Aggregationsstrategie. Wir können die neue Statistik mit dem Management Studio-Objekt-Explorer anzeigen:
Durch Doppelklicken auf das Statistikobjekt wird bestätigt, dass es aus der Kunden-ID-Spalte in der Ansicht erstellt wurde (keine Basistabelle):
Indexierte Aufrufe können die Kardinalitätsschätzung verbessern
Wir verwenden immer noch die Standard Edition, wir löschen und erstellen jetzt die indizierte Ansicht (wodurch auch die Ansichtsstatistiken gelöscht werden) und führen die Abfrage erneut aus, diesmal mit NOEXPAND
Hinweis auskommentiert:
SELECT CO.CustomerID, SUM(CO.OrderQty) FROM dbo.CustomerOrders AS CO --WITH (NOEXPAND) WHERE CO.ProductID BETWEEN 711 AND 718 GROUP BY CO.CustomerID;
Wie erwartet bei Verwendung der Standard Edition ohne NOEXPAND
, arbeitet der resultierende Abfrageplan mit den Basistabellen und nicht direkt mit der Ansicht:
Das Warndreieck am Root-Operator im obigen Plan macht uns auf einen potenziell nützlichen Index in der Tabelle „Sales Order Detail“ aufmerksam, der für unsere derzeitigen Zwecke nicht wichtig ist. Diese Kompilierung erstellt keine Statistiken zur indizierten Ansicht. Die einzige Statistik für die Ansicht nach der Abfragekompilierung ist diejenige, die dem gruppierten Index zugeordnet ist:
Die Planbaumansicht für die Abfrage zeigt, dass die Kardinalitätsschätzung für die beiden Tabellenscans und den Join korrekt ist, aber für die anderen Planoperatoren um einiges schlechter:
Verwenden der indizierten Ansicht mit einem NOEXPAND
hint führte zu genaueren Schätzungen für unsere Testabfrage, da qualitativ hochwertigere Informationen aus den Statistiken zur Ansicht verfügbar waren – insbesondere aus den mit dem Ansichtsindex verknüpften Statistiken.
Als allgemeine Regel lässt die Genauigkeit statistischer Informationen ziemlich schnell nach, wenn sie durchlaufen und von Abfrageplanoperatoren modifiziert werden. Einfache Verknüpfungen sind in dieser Hinsicht oft nicht schlecht, aber Informationen über das Ergebnis einer Aggregation sind oft nicht besser als eine fundierte Vermutung. Die Bereitstellung genauerer Informationen für den Abfrageoptimierer mithilfe von Statistiken zu indizierten Ansichten kann eine nützliche Technik zur Steigerung der Planqualität und -robustheit sein.
Eine Ansicht ohne NOEXPAND kann zu einem schlechteren Plan führen
Der oben gezeigte Abfrageplan (Standard Edition, ohne NOEXPAND
) ist tatsächlich weniger optimal, als wenn wir die Abfrage selbst für die Basistabellen geschrieben hätten, anstatt dem Abfrageoptimierer zu erlauben, die Ansicht zu erweitern. Die folgende Abfrage drückt dieselbe logische Anforderung aus, verweist jedoch nicht auf die Ansicht:
SELECT SOH.CustomerID, SUM(OrderQty) FROM Sales.SalesOrderHeader AS SOH JOIN Sales.SalesOrderDetail AS SOD ON SOD.SalesOrderID = SOH.SalesOrderID WHERE SOD.ProductID BETWEEN 711 AND 718 GROUP BY SOH.CustomerID;
Diese Abfrage erzeugt den folgenden Ausführungsplan:
Dieser Plan enthält einen Aggregationsvorgang weniger als zuvor. Bei Verwendung der Ansichtserweiterung war der Abfrageoptimierer leider nicht in der Lage, einen redundanten Aggregationsvorgang zu entfernen, was zu einem weniger effizienten Ausführungsplan führte. Die endgültige Kardinalitätsschätzung für die neue Abfrage ist auch etwas besser als wenn auf die indizierte Ansicht ohne NOEXPAND
verwiesen wurde :
Dennoch sind die besten Schätzungen immer noch diejenigen, die beim Referenzieren der indizierten Ansicht mit NOEXPAND
erzeugt werden (unten der Einfachheit halber wiederholt):
Enterprise Edition und Ansichtsabgleich
Auf einer Enterprise Edition-Instanz kann der Abfrageoptimierer möglicherweise eine indizierte Ansicht verwenden, selbst wenn die Abfrage die Ansicht nicht explizit erwähnt. Wenn der Optimierer in der Lage ist, einen Teil des Abfragebaums einer indizierten Ansicht zuzuordnen, kann er dies basierend auf seiner Schätzung der Kosten für die Verwendung der Ansicht tun oder nicht. Die View-Matching-Logik ist ziemlich clever, hat aber Grenzen, die in der Praxis ziemlich leicht zu erreichen sind. Selbst wenn der View-Matching erfolgreich ist, kann der Optimierer immer noch durch ungenaue Kostenschätzungen in die Irre geführt werden.
Der Abfragehinweis EXPAND VIEWS
Beginnend mit der selteneren der Möglichkeiten kann es Fälle geben, in denen eine Abfrage auf eine indizierte Ansicht verweist, aber ein besserer Plan würde durch den Zugriff auf die Basistabellen erhalten werden. Unter diesen Umständen ist der Abfragehinweis EXPAND VIEWS
kann verwendet werden:
SELECT CO.CustomerID, SUM(CO.OrderQty) FROM dbo.CustomerOrders AS CO WHERE CO.ProductID BETWEEN 711 AND 718 GROUP BY CO.CustomerID OPTION (EXPAND VIEWS);
In der Enterprise Edition erzeugt diese Abfrage denselben Plan wie in der Standard Edition, wenn NOEXPAND
Hinweis wurde weggelassen (einschließlich der redundanten Aggregationsoperation):
Nebenbei die EXPAND VIEWS
Hinweis ist meiner Meinung nach schlecht benannt. SQL Server erweitert Ansichtsdefinitionen in einer Abfrage immer, es sei denn, NOEXPAND
Hinweis angegeben. Die EXPAND VIEWS
hint deaktiviert Regeln im Optimierer, die Teile des erweiterten Baums wieder mit indizierten Ansichten abgleichen können. Wenn kein Hinweis vorhanden ist, erweitert SQL Server zunächst eine Ansicht auf ihre Basistabellendefinition und erwägt später den Abgleich wieder mit indizierten Ansichten. Ein besserer Name für die EXPAND VIEWS
Der Hinweis könnte DISABLE INDEXED VIEW MATCHING
gewesen sein , weil es genau das tut.
Die EXPAND VIEWS
Hinweis wird wahrscheinlich am häufigsten verwendet, um zu verhindern, dass eine Abfrage für Basistabellen mit einer indizierten Ansicht abgeglichen wird:
SELECT SOH.CustomerID, SUM(OrderQty) FROM Sales.SalesOrderHeader AS SOH JOIN Sales.SalesOrderDetail AS SOD ON SOD.SalesOrderID = SOH.SalesOrderID WHERE SOD.ProductID BETWEEN 711 AND 718 GROUP BY SOH.CustomerID OPTION (EXPAND VIEWS);
Der Abfragehinweis führt zu demselben Ausführungsplan und denselben Schätzungen wie bei der Verwendung der Standard Edition und derselben Nur-Basistabellen-Abfrage:
Enterprise View Matching und Statistiken
Selbst in der Enterprise Edition werden Nicht-Index-Ansichtsstatistiken immer noch nur erstellt, wenn NOEXPAND
Hinweis verwendet wird. Um es ganz klar zu sagen:Die Nur-Enterprise-View-Matching-Funktion führt niemals dazu, dass View-Statistiken erstellt oder aktualisiert werden. Es lohnt sich, dieses nicht intuitive Verhalten ein wenig zu untersuchen, da es überraschende Nebenwirkungen haben kann.
Wir führen jetzt unsere grundlegende Abfrage für die Ansicht einer Enterprise Edition-Instanz ohne Hinweise aus:
SELECT CO.CustomerID, SUM(CO.OrderQty) FROM dbo.CustomerOrders AS CO WHERE CO.ProductID BETWEEN 711 AND 718 GROUP BY CO.CustomerID;
Neu ist das Warndreieck beim View Clustered Index Seek. Der Tooltip zeigt die Details:
Wir haben kein NOEXPAND
verwendet hint, sodass Statistiken zur Spalte „Kunden-ID“ der indizierten Ansicht nicht automatisch erstellt wurden. Die Statistiken zur Kundennummer sind in diesem vereinfachten Beispiel eigentlich nicht besonders wichtig, aber das wird nicht immer der Fall sein.
Skurrile Kardinalitätsschätzungen
Die zweite interessante Sache ist, dass die Kardinalitätsschätzungen schlechter zu sein scheinen als alle Fälle, denen wir bisher begegnet sind, einschließlich der Beispiele der Standard Edition.
Es ist zunächst schwer zu erkennen, woher die Kardinalitätsschätzung für den View Clustered Index Seek (11.267) stammt. Wir würden erwarten, dass die Schätzung auf Produkt-ID-Histogramminformationen aus den Statistiken basiert, die mit dem View-Clustered-Index verknüpft sind. Der relevante Teil dieses Histogramms ist unten dargestellt:
DBCC SHOW_STATISTICS ('dbo.CustomerOrders', 'cuq') WITH HISTOGRAM;
Da die Tabelle seit der Erstellung der Statistik nicht geändert wurde, würden wir erwarten, dass die Schätzung eine einfache Summe von RANGE_ROWS und EQ_ROWS für Produkt-ID-Werte zwischen 711 und 718 ist (beachten Sie, dass die Schätzung die 28 RANGE_ROWS ausschließen sollte, die für den Eintrag 711 angezeigt werden da diese Zeilen unter dem Schlüsselwert 711 vorhanden sind). Die Summe der angezeigten EQ_ROWS ist 7.301. Dies ist genau die Anzahl der tatsächlich von der Ansicht zurückgegebenen Zeilen – woher kommt also die Schätzung von 11.267?
Die Antwort liegt in der Art und Weise, wie der Ansichtsabgleich derzeit funktioniert. Unsere Abfrage hat NOEXPAND
nicht angegeben Hinweis, daher basieren die anfänglichen Kardinalitätsschätzungen auf dem ansichtserweiterten Abfragebaum. Dies lässt sich am einfachsten erkennen, wenn Sie sich den geschätzten Plan für dieselbe Abfrage mit EXPAND VIEWS
noch einmal ansehen angegeben:
Der rot schattierte Bereich stellt den Teil des Baums dar, der durch die Ansichtszuordnungsaktivität ersetzt wird. Die Ausgangskardinalität dieses Bereichs beträgt 11.267. Der nicht schattierte Teil mit der Schätzung von 11.220 ist von der Ansichtsanpassung nicht betroffen. Dies sind genau die Schätzungen, die wir erklären wollten:
Der Ansichtsabgleich ersetzte einfach den rot schattierten Bereich durch eine logisch äquivalente Suche in der indizierten Ansicht. Es wurden keine statistischen Informationen aus der Ansicht verwendet, um die Kardinalitätsschätzung neu zu berechnen.
Bis zu einem gewissen Grad können Sie wahrscheinlich nachvollziehen, warum dies so funktionieren könnte:Im Allgemeinen gibt es wenig Grund zu der Annahme, dass eine Schätzung, die aus einem Satz statistischer Informationen berechnet wird, besser ist als eine andere. Es könnte argumentiert werden, dass die indizierten Ansichtsstatistiken hier eher genau sind als die nach dem Beitritt abgeleiteten Statistiken im rot schattierten Bereich, aber es könnte schwierig sein, dies zu verallgemeinern oder korrekt zu berücksichtigen, wie schnell verschiedene Quellen von Statistische Informationen können veraltet sein, wenn sich die zugrunde liegenden Daten ändern.
Man könnte auch argumentieren, dass wir ein NOEXPAND
verwendet hätten, wenn wir uns so sicher wären, dass die indizierten Ansichtsinformationen besser sind Hinweis.
Noch merkwürdigere Kardinalitätsschätzungen
Eine noch interessantere Situation ergibt sich bei der Enterprise Edition, wenn wir die Abfrage gegen die Basistabellen schreiben und uns auf den automatisierten View-Matching verlassen:
SELECT SOH.CustomerID, SUM(OrderQty) FROM Sales.SalesOrderHeader AS SOH JOIN Sales.SalesOrderDetail AS SOD ON SOD.SalesOrderID = SOH.SalesOrderID WHERE SOD.ProductID BETWEEN 711 AND 718 GROUP BY SOH.CustomerID;
Die fehlende Statistikwarnung ist dieselbe wie zuvor und hat dieselbe Erklärung. Das interessantere Merkmal ist, dass wir jetzt eine niedrigere Schätzung für die Anzahl der von View Clustered Index Seek erzeugten Zeilen (7.149) und eine erhöhte Schätzung für die Anzahl der von der Aggregation zurückgegebenen Zeilen (8.226) haben.
Um den Punkt zu unterstreichen:Dieser Abfrageplan scheint auf der Idee zu basieren, dass 7.149 Quellzeilen aggregiert werden können, um 8.226 Zeilen zu erzeugen!
Ein Teil der Erklärung ist die gleiche wie zuvor. Die EXPAND VIEWS
Abfrageplan, der den roten Bereich zeigt, der durch den View-Matching ersetzt wird, ist unten dargestellt:
Dies erklärt, woher die endgültige Schätzung von 8.226 kommt, aber was ist mit der Schätzung von 7.149 Zeilen? Nach der zuvor beschriebenen Logik scheint die Ansicht eine Schätzung von 11.267 Zeilen anzeigen zu müssen?
Die Antwort ist, dass die Schätzung von 7.149 eine Vermutung ist. Ja wirklich. Die indizierte Ansicht enthält insgesamt 79.433 Zeilen. Der magische Prozentsatz für das Prädikat Product ID BETWEEN beträgt 9 %, was 0,09 * 79433 =7148,97 Zeilen ergibt. Der SSMS-Abfrageplan zeigt, dass diese Berechnung sogar vor dem Runden genau richtig ist:
In dieser Situation scheint das SQL Server-Optimierungsprogramm eine Schätzung basierend auf der Kardinalität der indizierten Ansicht der Post-Join-Kardinalitätsschätzung aus der ersetzten Teilstruktur vorgezogen zu haben. Neugierig.
Zusammenfassung
Mit NOEXPAND
hint garantiert, dass im endgültigen Abfrageplan eine indizierte Ansicht verwendet wird, und ermöglicht die automatische Erstellung, Verwaltung und Verwendung von Nicht-Index-Statistiken durch den Abfrageoptimierer. Verwenden von NOEXPAND
stellt außerdem sicher, dass die anfänglichen Kardinalitätsschätzungen auf indizierten Ansichtsinformationen basieren und nicht aus Basistabellen abgeleitet werden.
Wenn NOEXPAND
nicht angegeben ist, werden Sichtreferenzen immer durch ihre Basistabellendefinitionen ersetzt, bevor die Abfragekompilierung beginnt (und daher vor der anfänglichen Kardinalitätsschätzung). Nur bei Enterprise-SKUs können indizierte Ansichten später im Optimierungsprozess wieder in die Abfragestruktur eingefügt werden.
Die EXPAND VIEWS
Der Abfragehinweis verhindert, dass der Optimierer den Abgleich der indizierten Ansicht der Enterprise Edition durchführt. Dies gilt unabhängig davon, ob die Abfrage ursprünglich auf eine indizierte Ansicht verwiesen hat oder nicht. Beim View-Matching kann unter Umständen eine vorhandene Kardinalitätsschätzung durch eine Schätzung ersetzt werden.
Statistiken, die in einer indizierten Ansicht als fehlend angezeigt werden, können manuell erstellt werden, aber der Optimierer verwendet sie im Allgemeinen nicht für Abfragen, die kein NOEXPAND
verwenden Hinweis.
Die Verwendung von indizierten Ansichten kann die Kardinalitätsschätzung verbessern, insbesondere wenn die Ansicht Verknüpfungen oder Aggregationen enthält. Abfragen haben die beste Chance, von genaueren Ansichtsstatistiken zu profitieren, wenn NOEXPAND
angegeben ist.