Creare viste indicizzate
Si applica a: SQL Server Database SQL di Azure Istanza gestita di SQL di Azure
Questo articolo descrive come creare indici in una vista. Il primo indice creato per una vista deve essere un indice cluster univoco. Dopo aver creato l'indice cluster univoco, è possibile creare più indici non cluster. La creazione di un indice cluster univoco per una vista consente un miglioramento delle prestazioni delle query, in quanto la vista viene archiviata nel database in modo analogo a una tabella con un indice cluster. Query Optimizer può utilizzare le viste indicizzate per accelerare l'esecuzione delle query. Non è necessario fare riferimento alla vista nella query affinché venga usata da Query Optimizer per una sostituzione.
Passaggi
Per la creazione e la corretta implementazione di una vista indicizzata, è fondamentale effettuare le operazioni seguenti:
- Verificare che le opzioni
SET
siano corrette per tutte le tabelle esistenti a cui verrà fatto riferimento nella vista. - Verificare che le opzioni
SET
della sessione siano impostate in modo corretto prima di creare qualsiasi tabella e la vista. - Verificare che la definizione della vista sia deterministica.
- Verificare che la tabella di base abbia lo stesso proprietario della vista.
- Creare la vista usando l'opzione
WITH SCHEMABINDING
. - Creare l'indice cluster univoco per la vista.
Durante l'esecuzione delle operazioni UPDATE
, DELETE
o INSERT
(Data Manipulation Language, DML) in una tabella cui viene fatto riferimento da un numero elevato di viste indicizzate o da un numero minore di viste indicizzate complesse, è necessario aggiornare anche le viste indicizzate cui viene fatto riferimento. Di conseguenza, è possibile che le prestazioni delle query DML si riducano notevolmente o, in alcuni casi, non venga prodotto un piano di query.
In questi scenari, testare le query DML prima dell'uso in produzione, analizzare il piano di query e ottimizzare/semplificare l'istruzione DML.
Opzioni SET necessarie per le viste indicizzate
La valutazione della stessa espressione può produrre risultati diversi nel motore di database se sono attive diverse opzioni SET
quando la query viene eseguita. Ad esempio, dopo aver impostato l'opzione SET
CONCAT_NULL_YIELDS_NULL
su ON
, l'espressione 'abc' + NULL
restituisce il valore NULL
. Tuttavia, dopo aver impostato CONCAT_NULL_YIELDS_NULL
su OFF
, la stessa espressione produce abc
.
Per essere certi che le viste possano essere gestite in modo corretto e restituiscano risultati coerenti, è necessario usare valori fissi per varie opzioni SET
delle viste indicizzate. Le opzioni SET
specificate nella tabella seguente devono essere impostate sui valori indicati nella colonna Required value
quando si verificano le seguenti condizioni:
- Vengono creati la vista e gli indici successivi nella vista.
- Le tabelle di base a cui si fa riferimento nella vista quando viene creata la vista stessa.
- Quando viene eseguita un'operazione di inserimento, aggiornamento o eliminazione su una qualsiasi tabella usata nella vista indicizzata, incluse operazioni quali la copia bulk, la replica e le query distribuite.
- Quando la vista indicizzata viene usata in Query Optimizer per generare il piano di query.
Opzioni SET | Valore obbligatorio | Valore server predefinito | Predefiniti OLE DB e ODBC predefinito |
Predefiniti DB-Library predefinito |
---|---|---|---|---|
ANSI_NULLS |
ON |
ON |
ON |
OFF |
ANSI_PADDING |
ON |
ON |
ON |
OFF |
ANSI_WARNINGS 1 |
ON |
ON |
ON |
OFF |
ARITHABORT |
ON |
ON |
OFF |
OFF |
CONCAT_NULL_YIELDS_NULL |
ON |
ON |
ON |
OFF |
NUMERIC_ROUNDABORT |
OFF |
OFF |
OFF |
OFF |
QUOTED_IDENTIFIER |
ON |
ON |
ON |
OFF |
1 L'impostazione di ANSI_WARNINGS
su ON
imposta in modo implicito ARITHABORT
su ON
.
Se si usa una connessione server OLE DB o ODBC, l'unico valore da modificare è l'impostazione ARITHABORT
. Tutti i valori DB-Library devono essere impostati in modo corretto a livello di server tramite sp_configure
oppure dall'applicazione tramite il comando SET
.
Importante
È consigliabile impostare l'opzione utente ARITHABORT
su ON
per l'intero server immediatamente dopo la creazione della prima vista indicizzata o del primo indice in una colonna calcolata in qualsiasi database del server.
Requisito di visualizzazione deterministico
La definizione di una vista indicizzata deve essere deterministica. Una vista è deterministica se tutte le espressioni nell'elenco di selezione, nonché nelle clausole WHERE
e GROUP BY
, sono deterministiche. Le espressioni deterministiche restituiscono sempre lo stesso risultato ogni volta che vengono valutate con un set specifico di valori di input. Nelle espressioni deterministiche è possibile usare solo funzioni deterministiche. La funzione DATEADD
, ad esempio, è deterministica perché restituisce sempre lo stesso risultato per un dato set di valori dei relativi tre parametri. GETDATE
non è deterministica perché viene sempre richiamata con lo stesso argomento, ma il valore restituito cambia ogni volta che viene eseguita.
Per determinare se una colonna della vista è deterministica, usare la proprietà IsDeterministic
della funzione COLUMNPROPERTY. Usare la proprietà IsPrecise
della funzione COLUMNPROPERTY
per determinare se una colonna deterministica in una vista con associazione di schema è precisa. COLUMNPROPERTY
restituisce 1
se TRUE
, 0
se FALSE
e NULL
per l'input non è valido. Questo significa che la colonna non è deterministica o non è precisa.
Se in un'espressione deterministica sono contenute espressioni float, il risultato esatto può dipendere dall'architettura del processore o dalla versione del microcodice. Per garantire l'integrità dei dati, le espressioni di questo tipo possono essere usate solo come colonne non chiave delle viste indicizzate. Le espressioni deterministiche che non contengono espressioni float sono definite precise. Nelle colonne chiave e nelle clausole WHERE
o GROUP BY
delle viste indicizzate è possibile usare solo espressioni deterministiche precise.
Requisiti aggiuntivi
Oltre alle impostazioni delle opzioni SET
e ai requisiti relativi alle funzioni deterministiche, è necessario che vengano soddisfatti i requisiti seguenti:
L'utente che esegue
CREATE INDEX
deve essere il proprietario della vista.Quando si crea l'indice, l'opzione dell'indice
IGNORE_DUP_KEY
deve essere impostata suOFF
(impostazione predefinita).I riferimenti alle tabelle devono essere specificati come nomi composti da due parti, ovvero
<schema>.<tablename>
nella definizione della vista.Le funzioni definite dall'utente a cui viene fatto riferimento nella vista devono essere create usando l'opzione
WITH SCHEMABINDING
.A qualsiasi funzione definita dall'utente a cui si fa riferimento nella vista deve essere fatto riferimento usando nomi in due parti,
<schema>.<function>
.La proprietà di accesso ai dati di una funzione definita dall'utente deve essere
NO SQL
e la proprietà di accesso esterno deve essereNO
.Le funzioni CLR (Common Language Runtime) possono essere incluse solo nell'elenco SELECT della vista ma non possono fare parte della definizione della chiave di indice cluster. Le funzioni CLR non possono essere incluse nella clausola
WHERE
della vista o nella clausolaON
di un'operazioneJOIN
nella vista.Le proprietà delle funzioni CLR e dei metodi di tipi CLR definiti dall'utente usati nella definizione della vista devono essere impostate come illustrato nella tabella seguente.
Proprietà Nota DETERMINISTIC = TRUE Deve essere dichiarata in modo esplicito come attributo del metodo di Microsoft .NET Framework. PRECISE = TRUE Deve essere dichiarata in modo esplicito come attributo del metodo di .NET Framework. DATA ACCESS = NO SQL Determinato impostando l'attributo DataAccess
suDataAccessKind.None
e l'attributoSystemDataAccess
suSystemDataAccessKind.None
.EXTERNAL ACCESS = NO Per le routine CLR il valore predefinito di questa proprietà è NO. La vista deve essere creata usando l'opzione
WITH SCHEMABINDING
.La vista deve contenere riferimenti solo a tabelle di base che si trovano nello stesso database della vista. La vista non può fare riferimento ad altre viste.
Se è presente
GROUP BY
, la definizione di VIEW deve contenereCOUNT_BIG(*)
e non deve contenereHAVING
. Queste restrizioni diGROUP BY
vengono applicate solo alla definizione della vista indicizzata. Una query può usare una vista indicizzata nel relativo piano di esecuzione anche se non soddisfa le restrizioni diGROUP BY
.Se la definizione della vista include una clausola
GROUP BY
, la chiave dell'indice cluster univoco può contenere riferimenti solo alle colonne specificate nella clausolaGROUP BY
.Nell'istruzione
SELECT
della definizione della vista non deve contenere la seguente sintassi Transact-SQL:Funzione Transact-SQL Possibili alternative COUNT
Utilizzare COUNT_BIG
.ROWSET
funzioni (OPENDATASOURCE
,OPENQUERY
,OPENROWSET
eOPENXML
)Media aritmetica ( AVG
)Usare COUNT_BIG
eSUM
come colonne separateFunzioni di aggregazione statistiche ( STDEV
,STDEVP
,VAR
eVARP
)Funzione SUM
che fa riferimento a un'espressione che ammette i valori NullUsare ISNULL
all'interno diSUM()
per far sì che l'espressione non ammetta i valori nullAltre funzioni di aggregazione ( MIN
,MAX
,CHECKSUM_AGG
eSTRING_AGG
)Funzioni di aggregazione definite dall'utente (SQL CLR) Clausola SELECT Elemento Transact-SQL Possibile alternativa WITH cte AS
Espressioni di tabella comuni (CTE) WITH
SELECT
Subqueries (Sottoquery) SELECT
SELECT [ <table>. ] *
Assegnare un nome esplicito alle colonne SELECT
SELECT DISTINCT
Utilizzare GROUP BY
.SELECT
SELECT TOP
SELECT
Clausola OVER
, che include funzioni di rango o funzioni finestra di aggregazioneFROM
LEFT OUTER JOIN
FROM
RIGHT OUTER JOIN
FROM
FULL OUTER JOIN
FROM
OUTER APPLY
FROM
CROSS APPLY
FROM
Espressioni di tabella derivate (ovvero, usando SELECT
nella clausolaFROM
)FROM
Self-join FROM
Variabili di tabella FROM
Funzioni con valori di tabella inline FROM
Funzioni con valori di tabella con più istruzioni FROM
PIVOT
,UNPIVOT
FROM
TABLESAMPLE
FROM
FOR SYSTEM_TIME
Eseguire direttamente una query sulla tabella di cronologia temporale WHERE
Predicati full-text ( CONTAINS
,FREETEXT
,CONTAINSTABLE
,FREETEXTTABLE
)GROUP BY
Operatori CUBE
,ROLLUP
oGROUPING SETS
Definire viste indicizzate separate per ogni combinazione di colonne GROUP BY
GROUP BY
HAVING
Operatori Set UNION
,UNION ALL
,EXCEPT
,INTERSECT
Usare rispettivamente OR
,AND NOT
eAND
nella clausolaWHERE
ORDER BY
ORDER BY
ORDER BY
OFFSET
Tipo di colonna di origine Possibile alternativa Tipi di colonna text, ntext e image con valore grande deprecati Eseguire la migrazione delle colonne rispettivamente a varchar(max), nvarchar(max) e varbinary(max). Colonne xml o FILESTREAM Colonne float 1 nella chiave di indice Set di colonne di tipo sparse 1 La vista indicizzata può contenere colonne di tipo float che, tuttavia, non possono essere incluse nella chiave di indice cluster.
Importante
Le viste indicizzate non sono supportate sulle query temporali, ovvero quelle che usano la clausola
FOR SYSTEM_TIME
.
Consigli per datetime e smalldatetime
Quando si fa riferimento a valori letterali stringa datetime e smalldatetime nelle viste indicizzate, è consigliabile convertire in modo esplicito il valore letterale nel tipo di dati desiderato usando uno stile del formato di data deterministico. Per un elenco degli stili del formato di data deterministici, vedere CAST e CONVERT. Per altre informazioni sulle espressioni deterministiche e non deterministiche, vedere la sezione Considerazioni in questa pagina.
Le espressioni che prevedono la conversione implicita di stringhe di caratteri nel tipo di dati datetime o smalldatetime sono considerate non deterministiche. Per altre informazioni, vedere Conversione non deterministica di stringhe di valori letterali in valori DATE.
Considerazioni sulle prestazioni con viste indicizzate
Durante l'esecuzione di DML, ad esempio di UPDATE
, DELETE
o INSERT
, in una tabella cui viene fatto riferimento da un numero elevato di viste indicizzate o da un numero minore di viste indicizzate molto complesse, è necessario aggiornare anche le viste indicizzate. Di conseguenza, è possibile che le prestazioni delle query DML si riducano notevolmente o, in alcuni casi, non venga prodotto un piano di query. In questi scenari, testare le query DML prima dell'uso in produzione, analizzare il piano di query e ottimizzare/semplificare l'istruzione DML.
Per impedire l'utilizzo di viste indicizzate nel motore di database, includere l'hint OPTION (EXPAND VIEWS) nella query. Inoltre, l'errata impostazione di una qualsiasi delle opzione elencate impedisce l'utilizzo degli indici delle viste in Query Optimizer. Per altre informazioni sull'hint OPTION (EXPAND VIEWS)
, vedere SELECT.
Considerazioni aggiuntive
L'impostazione dell'opzione
large_value_types_out_of_row
delle colonne in una vista indicizzata è ereditata dall'impostazione della colonna corrispondente nella tabella di base. Questo valore viene impostato mediante sp_tableoption. L'impostazione predefinita per le colonne generate da espressioni è0
. Ciò significa che i tipi per valori di grandi dimensioni vengono archiviati all'interno delle righe.È possibile creare viste indicizzate per una tabella partizionata, nonché partizionare questo tipo di viste.
Tutti gli indici di una vista vengono eliminati con la rimozione della vista. Tutti gli indici non cluster e tutte le statistiche create automaticamente nella vista vengono eliminati con l'eliminazione dell'indice cluster. Le statistiche create dall'utente nella vista vengono conservate. È possibile eliminare gli indici non cluster singolarmente. L'eliminazione dell'indice cluster nella vista determina la rimozione del set di risultati archiviato e la vista tornerà a essere elaborata come standard da Query Optimizer.
È possibile disabilitare gli indici di tabelle e viste. Quando l'indice cluster di una tabella è disabilitato, anche gli indici delle viste associate alla tabella sono disabilitati.
Autorizzazioni
Per poter creare una vista, un utente deve disporre dell'autorizzazione CREATE VIEW
per il database e dell'autorizzazione ALTER
per lo schema in cui viene creata la vista. Se la tabella di base si trova all'interno di uno schema diverso, per la tabella è necessaria almeno l'autorizzazione REFERENCES
. Se l'utente che crea l'indice è diverso dagli utenti che hanno creato la vista, per la creazione dell'indice è necessaria solo l'autorizzazione ALTER
per la vista (coperta da ALTER
nello schema).
Gli indici possono essere creati solo nelle viste con lo stesso proprietario della tabella o delle tabelle a cui si fa riferimento. Questa operazione è detta anche catena di proprietà intatta tra la vista e le tabelle. In genere, quando la tabella e la vista si trovano nello stesso schema, lo stesso proprietario dello schema si applica a tutti gli oggetti all'interno dello schema. È quindi possibile creare una vista e non esserne il proprietario. D'altra parte, è anche possibile che singoli oggetti all'interno di uno schema abbiano proprietari espliciti diversi. Se il proprietario è diverso dal proprietario dello schema, la colonna principal_id
in sys.tables
contiene un valore.
Creare una vista indicizzata: esempio T-SQL
Nell'esempio seguente vengono creati una vista e un indice per tale vista nel database AdventureWorks
.
--Set the options to support indexed views.
SET NUMERIC_ROUNDABORT OFF;
SET ANSI_PADDING,
ANSI_WARNINGS,
CONCAT_NULL_YIELDS_NULL,
ARITHABORT,
QUOTED_IDENTIFIER,
ANSI_NULLS ON;
--Create view with SCHEMABINDING.
IF OBJECT_ID('Sales.vOrders', 'view') IS NOT NULL
DROP VIEW Sales.vOrders;
GO
CREATE VIEW Sales.vOrders
WITH SCHEMABINDING
AS
SELECT SUM(UnitPrice * OrderQty * (1.00 - UnitPriceDiscount)) AS Revenue,
OrderDate,
ProductID,
COUNT_BIG(*) AS COUNT
FROM Sales.SalesOrderDetail AS od,
Sales.SalesOrderHeader AS o
WHERE od.SalesOrderID = o.SalesOrderID
GROUP BY OrderDate,
ProductID;
GO
--Create an index on the view.
CREATE UNIQUE CLUSTERED INDEX IDX_V1 ON Sales.vOrders (
OrderDate,
ProductID
);
GO
Le due query successive illustrano come usare la vista indicizzata, anche se la vista non è specificata nella clausola FROM
.
--This query can use the indexed view even though the view is
--not specified in the FROM clause.
SELECT SUM(UnitPrice * OrderQty * (1.00 - UnitPriceDiscount)) AS Rev,
OrderDate,
ProductID
FROM Sales.SalesOrderDetail AS od
INNER JOIN Sales.SalesOrderHeader AS o
ON od.SalesOrderID = o.SalesOrderID
AND o.OrderDate >= CONVERT(DATETIME, '05/01/2012', 101)
WHERE od.ProductID BETWEEN 700
AND 800
GROUP BY OrderDate,
ProductID
ORDER BY Rev DESC;
GO
--This query will also use the above indexed view.
SELECT OrderDate,
SUM(UnitPrice * OrderQty * (1.00 - UnitPriceDiscount)) AS Rev
FROM Sales.SalesOrderDetail AS od
INNER JOIN Sales.SalesOrderHeader AS o
ON od.SalesOrderID = o.SalesOrderID
AND o.OrderDate >= CONVERT(DATETIME, '03/01/2012', 101)
AND o.OrderDate < CONVERT(DATETIME, '04/01/2012', 101)
GROUP BY OrderDate
ORDER BY OrderDate ASC;
In questo esempio viene infine illustrata l'esecuzione di query direttamente dalla vista indicizzata. Prima di SQL Server 2016 (13.x) Service Pack 1, l'uso automatico di una vista indicizzata in Query Optimizer era supportato solo in edizioni specifiche di SQL Server. Nell'edizione SQL Server Standard è necessario usare l'hint per la query NOEXPAND
per eseguire direttamente una query sulla vista indicizzata. A partire da SQL Server 2016 (13.x) Service Pack 1, tutte le edizioni supportano l'uso automatico di una vista indicizzata. Database SQL di Azure e Istanza gestita di SQL di Azure supportano l'uso automatico di viste indicizzate senza specificare l'hint NOEXPAND
. Per ulteriori informazioni, vedere Hint di tabella (Transact-SQL).
--This query uses the indexed view directly, on Enterprise edition.
SELECT OrderDate, Revenue
FROM Sales.vOrders
WHERE OrderDate >= CONVERT(DATETIME, '03/01/2012', 101)
AND OrderDate < CONVERT(DATETIME, '04/01/2012', 101)
ORDER BY OrderDate ASC;
--This query uses the indexed view directly, with the NOEXPAND hint.
SELECT OrderDate, Revenue
FROM Sales.vOrders WITH (NOEXPAND)
WHERE OrderDate >= CONVERT(DATETIME, '03/01/2012', 101)
AND OrderDate < CONVERT(DATETIME, '04/01/2012', 101)
ORDER BY OrderDate ASC;
Per altre informazioni, vedere CREATE VIEW.