Indeksirani view-ovi

Objavljeno: 23-01-2014 | Autor: Milen Blagojević | Kategorija: View-ovi

0

Indeksirani view je u stvari view na kome je kreiran unique clustered index čime mogu značajno da se unaprede performanse upita nad njim. Da bi view mogao da postane indeksirani, mora da ispunjava određena pravila. Neka od bitnijih su (SQL Server 2008/2008 R2):

  • Indeksirani view ne sme referencirati ni jedan drugi view.
  • Indeksirani view mora biti kreiran sa SCHEMABINDING opcijom (što znači da se ne mogu menjati tabele koje ulaze u view)
  • Ukoliko se indeksirani view referencira na funkcije one takođe moraju biti kreirane sa SCHEMABINDING opcijom.
  • Sve funkcije na koje se referencira view moraju biti determinističke.
  • Ukoliko select lista sadrži agregatne izraze, mora biti definisano i GROUP BY.
  • Ako postoji GROUP BY, select mora sadržati COUNT_BIG(*) i ne sme postojati HAVING, CUBE ili ROLLUP.
  • Ne mogu se koristiti: Derived tabele, Rowset funkcije, UNION, ugnježdeni upiti, OUTER ili SELF join-i, TOP, ORDER BY, DISTINCT, COUNT(*) (COUNT_BIG(*) se može koristiti)
  • Ne mogu se koristiti sledeće funkcije: AVG, MAX, MIN, STDEV, STDEVP, VAR, VARP.
  • U SELECT naredbi ne sme postojati * ili tabela.*, imena kolona se moraju eksplicitno navesti
  • Kolona iz tabele se ne može koristi više puta (sem ako nije deo neke kalkulacije)

Za razliku od običnih view-ova koji se uvek izvršavaju u trenutku njihovog korišćenja, indeksirani view-ovi se čuvaju na diskovima i SQL Server ih tretira kao ostale tabele u čemu je i glavna prednost njihovog korišćenja.
Recimo da imamo upit:

SET STATISTICS TIME ON
SET STATISTICS IO ON
SELECT CF.[CrtacID], COUNT_BIG(*) BrojCrtanihFilmova
FROM [dbo].[CrtaniFilmovi] CF
INNER JOIN [dbo].[CrtaneEpizode] CE ON CF.CrtacID = CE.CrtacID 
GROUP BY CF.[CrtacID]

Statistike i exec plan:
Table ‘CrtaneEpizode’. Scan count 5, logical reads 5890, physical reads 20, read-ahead reads 5352, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CrtaniFilmovi’. Scan count 5, logical reads 1322, physical reads 25, read-ahead reads 1189, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 750 ms,  elapsed time = 2182 ms.

Kreirajmo sledeći view sa indeksom:

CREATE VIEW vw_CrtaniFilmBrojEpizoda
WITH SCHEMABINDING
AS
SELECT CF.[CrtacID], COUNT_BIG(*) BrojCrtanihFilmova
FROM [dbo].[CrtaniFilmovi] CF
INNER JOIN [dbo].[CrtaneEpizode] CE ON CF.CrtacID = CE.CrtacID
GROUP BY CF.[CrtacID]

GO
CREATE UNIQUE CLUSTERED INDEX IX_CrtaniFilmId  on vw_CrtaniFilmBrojEpizoda(CrtacID)

Nakon toga izvršavamo isti upit:

SET STATISTICS TIME ON
SET STATISTICS IO ON
SELECT CF.[CrtacID], COUNT_BIG(*) BrojCrtanihFilmova
FROM [dbo].[CrtaniFilmovi] CF
INNER JOIN [dbo].[CrtaneEpizode] CE ON CF.CrtacID = CE.CrtacID 
GROUP BY CF.[CrtacID]

Statistike i exec plan:
Table ‘vw_CrtaniFilmBrojEpizoda’. Scan count 1, logical reads 262, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms,  elapsed time = 866 ms.

Kao što vidite prilikom izvršavanja drugog upita sql server je čitao podatke iz view-a ne iz tabela koje su ušle u njega.

Treba još napomenuti da će sada i sve promene u podacima tabela koje ulaze u view biti zahtevnije zbog toga što se sada i sam view dinamički update-uje. Npr. insert u tabelu nad kojom ne postoji indeksirani view:

SET STATISTICS TIME ON
SET STATISTICS IO ON
INSERT INTO [dbo].[CrtaniFilmovi] ([CrtacID] ,[Naziv]  ,[Opis])
SELECT 100002, 'Naziv 100002', 'Opis 100002'

Statistike:
Table ‘CrtaniFilmovi’. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms,  elapsed time = 0 ms.

I kada postoji view:

SET STATISTICS TIME ON
SET STATISTICS IO ON
INSERT INTO [dbo].[CrtaniFilmovi] ([CrtacID] ,[Naziv]  ,[Opis])
SELECT 100002, 'Naziv 100002', 'Opis 100002'

Statistike:
Table ‘vw_CrtaniFilmBrojEpizoda’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 3, logical reads 7, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CrtaneEpizode’. Scan count 1, logical reads 5369, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘CrtaniFilmovi’. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
CPU time = 110 ms,  elapsed time = 114 ms.

Prilikom kreiranja indeksiranih view-a je potrebno voditi računa o tome koliko se često menjaju tabele koje ulaze u view, pogotovo ako ista tabela ulazi u više različitih indeksiranih view-a!

Indeksirani view se može kreirati na bilo kojoj ediciji SQL Servera, međutim samo u Enterprise i Datacentar (SQL Server 2008 R2) verzijama SQL Server Query Optimizer automatski moze da koristi kreirani view, u ostalim verzijama je potrebno koristiti hint: WITH (NOEXPAND)

SELECT [CrtacID]
      ,[BrojCrtanihFilmova]
  FROM [dbo].[vw_CrtaniFilmBrojEpizoda] WITH (NOEXPAND)