Database
 sql >> Database >  >> RDS >> Database

I 3 migliori suggerimenti che devi conoscere per scrivere viste SQL più veloci

Amico o nemico? Le visualizzazioni di SQL Server sono state oggetto di accesi dibattiti durante il mio primo anno di utilizzo di SQL Server. Hanno detto che era brutto perché era lento. Ma che ne dici di oggi?

Sei sulla stessa barca con cui ero io tanti anni fa? Quindi, unisciti a me in questo viaggio per svelare il vero affare sulle viste SQL in modo da poterle scrivere il più velocemente possibile.

Le viste SQL sono tabelle virtuali. I record in una vista sono il risultato di una query al suo interno. Ogni volta che le tabelle di base utilizzate nella vista vengono aggiornate, aggiorna anche la vista. In alcuni casi puoi anche INSERT, UPDATE ed DELETE record in una vista come tabella. Anche se non l'ho provato da solo.

Analogamente a una tabella, puoi CREATE, ALTER o DROP una vista. Puoi persino creare un indice, con alcune restrizioni.

Nota che ho usato SQL Server 2019 nei codici di esempio.

1. Conoscere l'uso corretto e improprio delle viste SQL

Innanzitutto, le basi.

A cosa servono le viste SQL?

È fondamentale. Se lo usi come un martello su un cacciavite, dimentica le visualizzazioni SQL più veloci. Innanzitutto, ricordiamo l'uso corretto:

  • Concentrare, semplificare e personalizzare la percezione che ogni utente ha del database.
  • Per consentire agli utenti di accedere alle uniche informazioni che devono vedere per motivi di sicurezza.
  • Per fornire la compatibilità con le versioni precedenti a una vecchia tabella o a un vecchio schema per non interrompere le app dipendenti. È temporaneo fino al completamento di tutte le modifiche necessarie.
  • Per partizionare i dati provenienti da server diversi. Pertanto, appaiono come se fossero una tabella di un server o istanza.

Come NON utilizzare le visualizzazioni di SQL Server?

  • Riutilizza la vista in un'altra vista che verrà riutilizzata in un'altra vista ancora. In breve, visioni profondamente nidificate. Il riutilizzo del codice in questo caso presenta alcuni inconvenienti.
  • Risparmia sulle sequenze di tasti. Si riferisce al primo, che riduce la pressione delle dita e sembra accelerare la codifica.

L'uso improprio delle visualizzazioni, se consentito, oscurerà il vero motivo per cui crei visualizzazioni. Come vedrai più avanti, i vantaggi reali superano i benefici percepiti di un uso improprio.

Esempio

Esaminiamo un esempio di Microsoft. Il dipendente vista da AdventureWorks . Ecco il codice:

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

Lo scopo di questa vista si concentra sulle informazioni di base dei dipendenti. Se necessario da parte del personale delle risorse umane, può essere visualizzato su una pagina web. È stato riutilizzato in altre viste?

Prova questo:

  1. In SQL Server Management Studio , cerca AdventureWorks banca dati.
  2. Espandi la cartella Visualizzazioni e cerca [HumanResources].[vEmployee].
  3. Fai clic con il pulsante destro del mouse e seleziona Visualizza dipendenze .

Se vedi un'altra vista a seconda di questa vista, che poi dipende da una vista diversa, Microsoft ci ha dato un cattivo esempio. Ma poi, non ci sono altre dipendenze di visualizzazione.

Passiamo al prossimo.

2. Sfatare il mito sulle viste SQL

Quando SQL Server elabora un SELECT da una vista , valuta il codice nella vista PRIMA di trattare la clausola WHERE o qualsiasi join nella query esterna. Con più tabelle unite, sarà lento rispetto a SELECT dalle tabelle di base con gli stessi risultati.

Almeno, è quello che mi è stato detto quando ho iniziato a usare SQL. Che sia un mito o meno, c'è solo un modo per scoprirlo. Passiamo a un esempio pratico.

Come funzionano le viste SQL

Microsoft non ci ha lasciato all'oscuro di dibattere all'infinito. Abbiamo gli strumenti per vedere come funzionano le query, come STATISTICS IO e il Piano di esecuzione effettivo . Li useremo in tutti i nostri esempi. Prendiamo il primo.

USE AdventureWorks
GO

SELECT * FROM HumanResources.vEmployee e
WHERE e.BusinessEntityID = 105

Per vedere cosa sta succedendo quando SQL Server elabora la vista, esaminiamo il Piano di esecuzione effettivo nella Figura 1. Lo confrontiamo con il codice CREATE VIEW per vEmployee nella sezione precedente.

Come puoi vedere, i primi nodi elaborati da SQL Server sono quelli che utilizzano INNER JOIN. Quindi, procede all'elaborazione dei LEFT OUTER JOIN.

Poiché non possiamo vedere un nodo Filter da nessuna parte per la clausola WHERE, deve trovarsi in uno di quei nodi. Se ispezioni tutte le proprietà dei nodi, vedrai la clausola WHERE elaborata nella tabella Employee. L'ho racchiuso in un riquadro nella Figura 1. Per dimostrare che è presente, vedere la Figura 2 per le Proprietà di quel nodo:

Analisi

Quindi, aveva l'istruzione SELECT in vEmployee vista è stata valutata o elaborata PRIMA che fosse applicata la clausola WHERE? Il Piano di Esecuzione mostra che non era così. Se lo fosse, dovrebbe apparire più vicino al nodo SELECT.

Quello che mi è stato detto era un mito. Stavo evitando qualcosa di buono a causa di un malinteso sul corretto utilizzo delle viste SQL.

Ora che sappiamo come SQL Server elabora un SELECT da una vista , la domanda rimane:è più lento che non utilizzare una vista?

SELEZIONA DA Visualizza vs. SELEZIONA DA Tavoli di base:quale funzionerà più velocemente?

Per prima cosa, dobbiamo estrarre l'istruzione SELECT all'interno di vEmployee visualizzare e produrre lo stesso risultato che abbiamo ottenuto utilizzando la vista. Il codice seguente mostra la stessa clausola WHERE:

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

Quindi, ispezioniamo STATISTICS IO ed eseguiamo un Confronta Showplan . Di quante risorse avrà bisogno una query da una vista rispetto all'esecuzione di query dalle tabelle di base? Vedi figura 3.

Qui, l'esecuzione di query da una vista o da tabelle di base consumerà le stesse letture logiche. Entrambi utilizzavano pagine da 19 * 8 KB. Sulla base di questo, è un pareggio su chi è più veloce. In altre parole, l'utilizzo di una vista non danneggerà le prestazioni. Confrontiamo il Piano di esecuzione effettivo di entrambi utilizzando il Confronta Showplan :

Vedi la parte ombreggiata del diagramma? Che ne dici di QueryPlanHash di entrambi? Poiché entrambe le query hanno QueryPlanHash uguale e le stesse operazioni, le tabelle di visualizzazione o di base verranno elaborate allo stesso modo da SQL Server .

Le stesse letture logiche e lo stesso piano del campione ci dicono che entrambi funzioneranno allo stesso modo. Pertanto, avere letture logiche elevate rallenterà la query indipendentemente dal fatto che utilizzi le visualizzazioni o meno. Conoscere questo fatto ti aiuterà a risolvere il problema e rendere la tua visualizzazione più veloce.

Sfortunatamente, ci sono delle brutte notizie.

Unire viste SQL alle tabelle

Quello che hai visto in precedenza è un SELECT da una vista senza join. Tuttavia, cosa succede se unisci una tabella a una vista?

Esaminiamo un altro esempio. Questa volta utilizziamo vSalesPerson visualizza in AdventureWorks – un elenco di venditori con informazioni di contatto e quota di vendita. Ancora una volta, confrontiamo l'istruzione con una SELECT dalle tabelle di base:

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

Se pensi che sarà anche lo stesso, controlla STATISTICS IO:

Sorpreso? Entrare a far parte di vSalesPerson visualizzare con SalesOrderHeader table ha bisogno di enormi risorse (28.240 x 8 KB) rispetto al solo utilizzo delle tabelle di base (774 x 8 KB). Si noti inoltre che includeva alcuni tavoli di cui non avevamo bisogno (i tavoli all'interno di caselle rosse). Per non parlare di letture logiche più elevate su SalesOrderHeader quando si utilizza la vista.

Ma non finisce qui.

Il piano di esecuzione effettivo rivela di più

Si noti il ​​piano di esecuzione effettivo della query alle tabelle di base:

L'illustrazione sembra mostrare un piano di esecuzione abbastanza normale. Ma dai un'occhiata a quello con la vista:

Il piano di esecuzione in Figura 7 coincide con STATISTICS IO in Figura 5. Possiamo vedere le tabelle che non ci servono dalla vista. C'è anche una Ricerca chiave nodo con una stima di riga che è più di mille record fuori rispetto alle righe effettive. Infine, viene visualizzato anche un avviso nel nodo SELECT. Cosa potrebbe essere?

Che cos'è quella ExcessiveGrant avviso nel nodo SELECT?

Una concessione eccessiva si verifica quando la memoria massima utilizzata è troppo piccola rispetto alla memoria concessa. In questo caso sono stati concessi 1024 KB, ma sono stati utilizzati solo 16 KB.

Concessione memoria è la quantità stimata di memoria in KB richiesta per eseguire il piano.

Potrebbero essere le stime sbagliate nella Ricerca chiave nodo e/o l'inclusione delle tabelle di cui non avevamo bisogno nel piano che ha causato ciò. Inoltre, la memoria troppo concessa può causare il blocco. I restanti 1008 KB avrebbero potuto essere utili per altre operazioni.

Alla fine, qualcosa è andato storto quando ti sei unito alla vista con una tabella. Non dobbiamo affrontare questi problemi se eseguiamo query dalle tabelle di base.

Da asporto

Era una lunga spiegazione. Tuttavia, sappiamo che le viste non vengono valutate o elaborate PRIMA di una clausola WHERE o dei join nella query esterna. Abbiamo anche dimostrato che entrambi si sarebbero comportati allo stesso modo.

D'altra parte, c'è un caso in cui uniamo una vista a una tabella. Utilizza join di tabelle di cui non abbiamo bisogno dalla vista. Sono invisibili per noi a meno che non controlliamo STATISTICS IO e l'Actual Execution Plan. Tutto ciò può compromettere le prestazioni e i problemi potrebbero venire dal nulla.

Pertanto:

  • Dovremmo sapere come funzionano le query, incluse le visualizzazioni, dall'interno.
  • STATISTICS IO e Actual Execution Plans riveleranno come funzioneranno le query e le visualizzazioni.
  • Non possiamo semplicemente unire una vista a una tabella e riutilizzarla con noncuranza. Controlla sempre STATISTICS IO e Actual Execution Plans! Invece di riutilizzare le viste e nidificarle per una produttività di codifica "migliorata", utilizzo un IntelliSense e lo strumento di completamento del codice come SQL Complete.

Possiamo quindi essere sicuri di non scrivere visualizzazioni che avranno risultati corretti ma funzioneranno come una lumaca.

3. Prova le visualizzazioni indicizzate

Le viste indicizzate sono ciò che suggerisce il nome. Può dare un aumento delle prestazioni alle istruzioni SELECT. Ma come gli indici delle tabelle, può influire sulle prestazioni se le tabelle di base sono grandi e continuamente aggiornate.

Per vedere come le viste indicizzate possono migliorare le prestazioni delle query, esaminiamo la vStateProvinceCountryRegion visualizza in AdventureWorks . La vista è indicizzata su StateProvinceID e Codice Paese . È un indice cluster univoco.

Confrontiamo le STATISTICHE IO della vista che non ha l'indice e che ha un indice. Con questo, impariamo quante pagine da 8 KB leggerà il nostro SQL Server:

La figura mostra che avere un indice nella vStateProvinceCountryRegion view riduce della metà le letture logiche. È un miglioramento del 50% rispetto alla mancanza di un indice.

È bello sentirlo.

Tuttavia, ancora una volta, non aggiungere indici alle tue visualizzazioni con noncuranza. Oltre ad avere un lungo elenco di regole rigorose per avere 1 indice cluster univoco, può influire negativamente sulle prestazioni, proprio come aggiungere indici alle tabelle in modo sbarazzino. Inoltre, controlla STATISTICS IO se c'è una diminuzione delle letture logiche dopo aver aggiunto l'indice.

Da asporto

Come abbiamo visto nel nostro esempio, le viste indicizzate possono migliorare le prestazioni delle viste SQL.

Suggerimento BONUS

Proprio come qualsiasi altra query, le viste SQL verranno eseguite velocemente se:

  • Le statistiche sono aggiornate
  • Sono stati aggiunti gli indici mancanti
  • Gli indici vengono deframmentati
  • Gli indici hanno utilizzato il FILLFACTOR corretto

Conclusione

Le visualizzazioni SQL sono buone o cattive?

Le viste SQL sono buone se le scriviamo correttamente e controlliamo per vedere come verranno elaborate. Abbiamo strumenti come STATISTICS IO e Actual Execution Plan:usali! Le visualizzazioni indicizzate possono anche migliorare le prestazioni.

Ti piace questo post? Per favore, condividi un po' di affetto sulla tua piattaforma di social media preferita.