Freund oder Feind? SQL Server-Ansichten waren Gegenstand hitziger Debatten, als ich in meinem ersten Jahr mit SQL Server arbeitete. Sie sagten, es sei schlecht, weil es langsam sei. Aber wie sieht es heute aus?
Sitzen Sie auf demselben Boot wie ich vor vielen Jahren? Dann begleiten Sie mich auf dieser Reise, um das wahre Problem mit SQL-Ansichten aufzudecken, damit Sie sie so schnell wie möglich schreiben können.
SQL-Ansichten sind virtuelle Tabellen. Die Datensätze in einer Ansicht sind das Ergebnis einer darin enthaltenen Abfrage. Immer wenn die in der Ansicht verwendeten Basistabellen aktualisiert werden, wird auch die Ansicht aktualisiert. In einigen Fällen können Sie Datensätze in einer Ansicht auch als Tabelle einfügen, aktualisieren und löschen. Obwohl ich das selbst noch nicht ausprobiert habe.
Ähnlich wie bei einer Tabelle können Sie eine Ansicht ERSTELLEN, ÄNDERN oder LÖSCHEN. Mit einigen Einschränkungen können Sie sogar einen Index erstellen.
Beachten Sie, dass ich SQL Server 2019 in den Beispielcodes verwendet habe.
1. Kennen Sie die richtige und falsche Verwendung von SQL-Ansichten
Zuerst die Grundlagen.
Wozu dienen SQL-Ansichten?
Es ist wichtig. Wenn Sie es als Hammer für einen Schraubenzieher verwenden, vergessen Sie schnellere SQL-Ansichten. Erinnern wir uns zunächst an die richtige Verwendung:
- Zur Fokussierung, Vereinfachung und Anpassung der Wahrnehmung, die jeder Benutzer von der Datenbank hat.
- Um den Benutzern Zugriff auf die einzigen Informationen zu gewähren, die sie aus Sicherheitsgründen sehen müssen.
- Um Abwärtskompatibilität zu einer alten Tabelle oder einem alten Schema bereitzustellen, um abhängige Apps nicht zu beschädigen. Es ist vorübergehend, bis alle erforderlichen Änderungen abgeschlossen sind.
- Zum Partitionieren von Daten, die von verschiedenen Servern kommen. Daher erscheinen sie so, als wären sie eine Tabelle von einem Server oder einer Instanz.
Wie sollte man SQL Server-Ansichten NICHT verwenden?
- Die Ansicht in einer anderen Ansicht wiederverwenden, die in einer weiteren Ansicht wiederverwendet wird. Kurz gesagt, tief verschachtelte Ansichten. Die Wiederverwendung des Codes hat in diesem Fall einige Nachteile.
- Spare Tastenanschläge. Es bezieht sich auf den ersten, der den Fingerdruck reduziert und das Codieren zu beschleunigen scheint.
Die unsachgemäße Verwendung von Ansichten, sofern zulässig, verschleiert den wahren Grund, warum Sie Ansichten erstellen. Wie Sie später sehen werden, überwiegen die wirklichen Vorteile die wahrgenommenen Vorteile einer unsachgemäßen Verwendung.
Beispiel
Sehen wir uns ein Beispiel von Microsoft an. Der vMitarbeiter Ansicht von AdventureWorks . Hier ist der Code:
-- Employee names and basic contact information
CREATE VIEW [HumanResources].[vEmployee]
AS
SELECT
e.[BusinessEntityID]
,p.[Title]
,p.[FirstName]
,p.[MiddleName]
,p.[LastName]
,p.[Suffix]
,e.[JobTitle]
,pp.[PhoneNumber]
,pnt.[Name] AS [PhoneNumberType]
,ea.[EmailAddress]
,p.[EmailPromotion]
,a.[AddressLine1]
,a.[AddressLine2]
,a.[City]
,sp.[Name] AS [StateProvinceName]
,a.[PostalCode]
,cr.[Name] AS [CountryRegionName]
,p.[AdditionalContactInfo]
FROM [HumanResources].[Employee] e
INNER JOIN [Person].[Person] p
ON p.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[BusinessEntityAddress] bea
ON bea.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[Address] a
ON a.[AddressID] = bea.[AddressID]
INNER JOIN [Person].[StateProvince] sp
ON sp.[StateProvinceID] = a.[StateProvinceID]
INNER JOIN [Person].[CountryRegion] cr
ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
LEFT OUTER JOIN [Person].[PersonPhone] pp
ON pp.BusinessEntityID = p.[BusinessEntityID]
LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
ON pp.[PhoneNumberTypeID] = pnt.[PhoneNumberTypeID]
LEFT OUTER JOIN [Person].[EmailAddress] ea
ON p.[BusinessEntityID] = ea.[BusinessEntityID];
GO
Der Zweck dieser Ansicht konzentriert sich auf grundlegende Mitarbeiterinformationen. Wenn es von einem Personalmitarbeiter benötigt wird, kann es auf einer Webseite angezeigt werden. Wurde es in anderen Ansichten wiederverwendet?
Versuchen Sie Folgendes:
- Im SQL Server Management Studio , suchen Sie nach AdventureWorks Datenbank.
- Erweitern Sie den Ordner "Ansichten" und suchen Sie nach [HumanResources].[vEmployee].
- Klicken Sie mit der rechten Maustaste darauf und wählen Sie Abhängigkeiten anzeigen .
Wenn Sie abhängig von dieser Ansicht eine andere Ansicht sehen, die dann von einer anderen Ansicht abhängt, hat uns Microsoft ein schlechtes Beispiel gegeben. Aber dann gibt es keine anderen Ansichtsabhängigkeiten.
Kommen wir zum nächsten.
2. Entlarven Sie den Mythos über SQL-Ansichten
Wenn SQL Server ein SELECT aus einer Ansicht verarbeitet , wertet es den Code in der Ansicht aus, BEVOR es sich mit der WHERE-Klausel oder einem Join in der äußeren Abfrage befasst. Wenn mehr Tabellen verbunden werden, wird es im Vergleich zu einem SELECT aus Basistabellen langsam mit den gleichen Ergebnissen.
Zumindest wurde es mir gesagt, als ich anfing, SQL zu verwenden. Ob es sich um einen Mythos handelt oder nicht, es gibt nur einen Weg, dies herauszufinden. Kommen wir zu einem praktischen Beispiel.
Wie SQL-Ansichten funktionieren
Microsoft hat uns nicht im Dunkeln gelassen, um endlos zu debattieren. Wir haben die Tools, um zu sehen, wie Abfragen funktionieren, wie z. B. STATISTICS IO und der Tatsächliche Ausführungsplan . Wir werden diese in allen unseren Beispielen verwenden. Nehmen wir den ersten.
USE AdventureWorks
GO
SELECT * FROM HumanResources.vEmployee e
WHERE e.BusinessEntityID = 105
Um zu sehen, was passiert, wenn SQL Server die Ansicht verarbeitet, sehen wir uns den Aktuellen Ausführungsplan an in Abbildung 1. Wir vergleichen es mit dem CREATE VIEW-Code für vEmployee im vorherigen Abschnitt.
Wie Sie sehen können, sind die ersten von SQL Server verarbeiteten Knoten diejenigen, die INNER JOIN verwenden. Dann fährt es mit der Verarbeitung der LEFT OUTER JOINs fort.
Da wir für die WHERE-Klausel nirgendwo einen Filterknoten sehen können, muss sie sich in einem dieser Knoten befinden. Wenn Sie die Eigenschaften aller Knoten untersuchen, sehen Sie, dass die WHERE-Klausel in der Employee-Tabelle verarbeitet wird. Ich habe es in Abbildung 1 in ein Kästchen eingeschlossen. Um zu beweisen, dass es da ist, siehe Abbildung 2 für die Eigenschaften dieses Knotens:
Analyse
Hatte also die SELECT-Anweisung im vEmployee Die Ansicht wurde ausgewertet oder verarbeitet, BEVOR die WHERE-Klausel angewendet wurde? Der Ausführungsplan zeigt, dass dies nicht der Fall war. Wenn ja, sollte es am nächsten zum SELECT-Knoten erscheinen.
Was mir gesagt wurde, war ein Mythos. Ich habe etwas Gutes vermieden, weil ich die Verwendung der richtigen SQL-Ansichten missverstanden habe.
Jetzt wissen wir, wie SQL Server ein SELECT aus einer Ansicht verarbeitet , bleibt die Frage:Ist es langsamer, als eine Ansicht nicht zu verwenden?
SELECT FROM View vs. SELECT FROM Base Tables – welche wird schneller laufen?
Zuerst müssen wir die SELECT-Anweisung in vEmployee extrahieren view und erzeugen das gleiche Ergebnis, das wir bei der Verwendung der Ansicht hatten. Der folgende Code zeigt dieselbe WHERE-Klausel:
USE AdventureWorks
GO
-- SELECT FROM a view
SELECT * FROM HumanResources.vEmployee e
WHERE e.BusinessEntityID = 105
-- SELECT FROM Base Tables
SELECT
e.[BusinessEntityID]
,p.[Title]
,p.[FirstName]
,p.[MiddleName]
,p.[LastName]
,p.[Suffix]
,e.[JobTitle]
,pp.[PhoneNumber]
,pnt.[Name] AS [PhoneNumberType]
,ea.[EmailAddress]
,p.[EmailPromotion]
,a.[AddressLine1]
,a.[AddressLine2]
,a.[City]
,sp.[Name] AS [StateProvinceName]
,a.[PostalCode]
,cr.[Name] AS [CountryRegionName]
,p.[AdditionalContactInfo]
FROM [HumanResources].[Employee] e
INNER JOIN [Person].[Person] p
ON p.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[BusinessEntityAddress] bea
ON bea.[BusinessEntityID] = e.[BusinessEntityID]
INNER JOIN [Person].[Address] a
ON a.[AddressID] = bea.[AddressID]
INNER JOIN [Person].[StateProvince] sp
ON sp.[StateProvinceID] = a.[StateProvinceID]
INNER JOIN [Person].[CountryRegion] cr
ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
LEFT OUTER JOIN [Person].[PersonPhone] pp
ON pp.BusinessEntityID = p.[BusinessEntityID]
LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
ON pp.[PhoneNumberTypeID] = pnt.[PhoneNumberTypeID]
LEFT OUTER JOIN [Person].[EmailAddress] ea
ON p.[BusinessEntityID] = ea.[BusinessEntityID]
WHERE e.BusinessEntityID = 105
Dann prüfen wir die STATISTIK IO und erstellen einen Showplan vergleichen . Wie viele Ressourcen benötigt eine Abfrage aus einer Ansicht im Vergleich zur Abfrage aus Basistabellen? Siehe Abbildung 3.
Hier verbraucht das Abfragen aus einer Ansicht oder aus Basistabellen die gleichen logischen Lesevorgänge. Beide verwendeten 19 * 8KB-Seiten. Basierend darauf ist es ein Gleichstand, wer schneller ist. Mit anderen Worten, die Verwendung einer Ansicht schadet der Leistung nicht. Vergleichen wir den Tatsächlichen Ausführungsplan von beiden mit dem Showplan vergleichen :
Sehen Sie den schraffierten Teil des Diagramms? Wie wäre es mit dem QueryPlanHash von beiden? Da beide Abfragen denselben QueryPlanHash haben und die gleichen Operationen, entweder Ansichts- oder Basistabellen werden von SQL Server gleich verarbeitet .
Die gleichen logischen Lesevorgänge und der gleiche Plan der Stichprobe sagen uns, dass beide die gleiche Leistung erbringen. Wenn Sie also viele logische Lesevorgänge haben, wird Ihre Abfrage langsam ausgeführt, unabhängig davon, ob Sie Ansichten verwenden oder nicht. Wenn Sie diese Tatsache kennen, können Sie das Problem beheben und Ihre Ansicht schneller ausführen.
Leider gibt es schlechte Nachrichten.
Verbinden von SQL-Ansichten mit Tabellen
Was Sie zuvor gesehen haben, ist ein SELECT aus einer Ansicht ohne Joins darin. Was aber, wenn Sie eine Tabelle mit einer Ansicht verknüpfen?
Sehen wir uns ein weiteres Beispiel an. Dieses Mal verwenden wir die vSalesPerson in AdventureWorks ansehen – eine Verkäuferliste mit Kontaktinformationen und Verkaufsquote. Wieder vergleichen wir die Anweisung mit einem SELECT aus Basistabellen:
-- get the total sales orders for each salesperson
-- using the view joined with SalesOrderHeader
SELECT
sp.FirstName
,sp.MiddleName
,sp.LastName
,SUM(soh.TotalDue) AS TotalSalesOrders
FROM Sales.vSalesPerson sp
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
GROUP BY sp.LastName, sp.MiddleName, sp.FirstName
-- using base tables
SELECT
p.FirstName
,p.MiddleName
,p.LastName
,SUM(soh.TotalDue) AS TotalSalesOrders
FROM sales.SalesPerson sp
INNER JOIN Person.Person p ON sp.BusinessEntityID = P.BusinessEntityID
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
GROUP BY p.LastName, p.MiddleName, p.FirstName
Wenn Sie glauben, dass es auch so sein wird, überprüfen Sie die STATISTIK IO:
Überrascht? Dem vSalesPerson beitreten Ansicht mit dem SalesOrderHeader Tabelle benötigt enorme Ressourcen (28.240 x 8 KB) im Vergleich zur Verwendung der Basistabellen (774 x 8 KB). Beachten Sie auch, dass es einige Tabellen enthielt, die wir nicht brauchten (die Tabellen in roten Kästchen). Ganz zu schweigen von höheren logischen Lesezugriffen auf SalesOrderHeader bei Verwendung der Ansicht.
Aber das ist noch nicht alles.
Der tatsächliche Ausführungsplan verrät mehr
Beachten Sie den tatsächlichen Ausführungsplan der Abfrage zu Basistabellen:
Die Abbildung scheint einen ziemlich normalen Ausführungsplan zu zeigen. Aber sehen Sie sich das mit der Ansicht an:
Der Ausführungsplan in Abbildung 7 stimmt mit dem STATISTICS IO in Abbildung 5 überein. Wir können die Tabellen, die wir nicht benötigen, aus der Ansicht sehen. Es gibt auch eine Schlüsselsuche Knoten mit einer Zeilenschätzung, die mehr als tausend Datensätze von den tatsächlichen Zeilen entfernt ist. Abschließend erscheint auch eine Warnung im SELECT-Knoten. Was könnte das sein?
Was ist das für ein ExcessiveGrant? Warnung im SELECT-Knoten?
Eine übermäßige Gewährung tritt auf, wenn der maximal verwendete Arbeitsspeicher im Vergleich zum gewährten Arbeitsspeicher zu klein ist. In diesem Fall wurden 1024 KB gewährt, aber nur 16 KB verwendet.
Memory Grant ist die geschätzte Menge an Arbeitsspeicher in KB, die zum Ausführen des Plans erforderlich ist.
Es könnten die falschen Schätzungen in der Schlüsselsuche sein Knoten und/oder die Aufnahme der Tabellen, die wir nicht in den Plan benötigten, die dies verursachten. Außerdem kann zu viel Arbeitsspeicher zu Blockierungen führen. Die restlichen 1008 KB hätten für andere Operationen nützlich sein können.
Irgendwann ist etwas schief gelaufen, als Sie der Ansicht eine Tabelle hinzugefügt haben. Wir müssen uns mit diesen Problemen nicht befassen, wenn wir die Basistabellen abfragen.
Imbiss
Es war eine lange Erklärung. Wir wissen jedoch, dass Views nicht ausgewertet oder verarbeitet werden, BEVOR eine WHERE-Klausel oder Joins in der äußeren Abfrage ausgewertet werden. Wir haben auch bewiesen, dass beide die gleiche Leistung erbringen würden.
Andererseits gibt es einen Fall, in dem wir eine Ansicht mit einer Tabelle verbinden. Es verwendet Verknüpfungen von Tabellen, die wir nicht aus der Ansicht benötigen. Sie sind für uns unsichtbar, es sei denn, wir überprüfen die STATISTICS IO und den Actual Execution Plan. All dies kann die Leistung beeinträchtigen und Probleme können aus dem Nichts kommen.
Deshalb:
- Wir sollten wissen, wie Abfragen, einschließlich Ansichten, von innen funktionieren.
- STATISTICS IO und Actual Execution Plans zeigen, wie Abfragen und Ansichten funktionieren werden.
- Wir können eine Ansicht nicht einfach mit einer Tabelle verknüpfen und sie achtlos wiederverwenden. Überprüfen Sie immer die STATISTICS IO und Actual Execution Plans! Anstatt Ansichten wiederzuverwenden und sie für eine „verbesserte“ Codierungsproduktivität zu verschachteln, verwende ich IntelliSense und das Codevervollständigungstool wie SQL Complete.
Wir können dann sicher sein, dass wir keine Aufrufe schreiben, die korrekte Ergebnisse liefern, aber wie eine Schnecke laufen.
3. Probieren Sie indizierte Ansichten
ausIndizierte Ansichten sind das, was der Name schon sagt. Es kann die Leistung von SELECT-Anweisungen steigern. Aber wie bei Tabellenindizes kann es die Leistung beeinträchtigen, wenn die Basistabellen groß sind und ständig aktualisiert werden.
Um zu sehen, wie indizierte Ansichten die Abfrageleistung verbessern können, untersuchen wir die vStateProvinceCountryRegion in AdventureWorks ansehen . Die Ansicht wird auf StateProvinceID indiziert und CountryRegionCode . Es ist ein gruppierter, eindeutiger Index.
Vergleichen wir den STATISTICS IO der Ansicht, die keinen Index hat, und die einen Index hat. Damit erfahren wir, wie viele 8-KB-Seiten unser SQL-Server lesen wird:
Die Abbildung zeigt, dass ein Index in vStateProvinceCountryRegion vorhanden ist view reduziert logische Lesevorgänge um die Hälfte. Es ist eine 50 %ige Verbesserung im Vergleich dazu, keinen Index zu haben.
Das ist gut zu hören.
Fügen Sie Ihren Ansichten dennoch nicht sorglos Indizes hinzu. Abgesehen davon, dass es eine lange Liste strenger Regeln gibt, um einen eindeutigen, gruppierten Index zu haben, kann dies die Leistung beeinträchtigen, ähnlich wie das unbeschwerte Hinzufügen von Indizes zu Tabellen. Überprüfen Sie auch den STATISTICS IO, wenn nach dem Hinzufügen des Indexes eine Abnahme der logischen Lesevorgänge auftritt.
Imbiss
Wie wir in unserem Beispiel gesehen haben, können indizierte Ansichten die Leistung von SQL-Ansichten verbessern.
BONUS-Tipp
Genau wie jede andere Abfrage werden SQL-Ansichten schnell ausgeführt, wenn:
- Statistiken werden aktualisiert
- Fehlende Indizes werden hinzugefügt
- Indizes werden defragmentiert
- Indizes verwendeten den richtigen FILLFACTOR
Schlussfolgerung
Sind SQL-Ansichten gut oder schlecht?
SQL-Ansichten sind gut, wenn wir sie richtig schreiben und prüfen, wie sie verarbeitet werden. Wir haben Tools wie STATISTICS IO und Actual Execution Plan – nutzen Sie sie! Indizierte Aufrufe können auch die Leistung verbessern.
Gefällt Ihnen dieser Beitrag? Bitte teilen Sie etwas Liebe auf Ihrer bevorzugten Social-Media-Plattform.