Résolution de vues

Le processeur de requêtes SQL Server traite différemment les vues indexées et les vues non indexées :

  • Les lignes des vues indexées sont stockées dans la base de données dans le même format qu'une table. Si l'optimiseur de requête décide d'utiliser une vue indexée dans un plan de requête, celle-ci est traitée de la même façon qu'une table de base.

  • Seule la définition d'une vue non indexée est stockée, tandis que les lignes de la vue ne le sont pas. L'optimiseur de requête incorpore la logique de la définition de la vue dans le plan d'exécution qu'il construit pour l'instruction SQL faisant référence à la vue non indexée.

La logique utilisée par l'optimiseur de requête SQL Server pour déterminer quand utiliser une vue indexée est similaire à la logique employée pour décider quand utiliser un index sur une table. Si les données de la vue indexée couvrent tout ou partie de l'instruction SQL et si l'optimiseur de requête détermine qu'un index sur la vue est le chemin d'accès le moins coûteux, l'optimiseur choisit l'index, que la vue soit référencée ou non par son nom dans la requête. Pour plus d'informations, consultez Résolution d'index sur les vues.

Lorsqu'une instruction SQL fait référence à une vue non indexée, l'analyseur et l'optimiseur de requête analysent la source de l'instruction SQL et de la vue, puis les résolvent dans un même plan d'exécution. Il n'y a pas de plans distincts pour l'instruction SQL et pour la vue.

Imaginons par exemple la vue suivante :

USE AdventureWorks;
GO
CREATE VIEW EmployeeName AS
SELECT h.EmployeeID, c.LastName, c.FirstName
FROM HumanResources.Employee AS h 
JOIN Person.Contact AS c
ON h.ContactID = c.ContactID;
GO

Sur la base de cette vue, les deux instructions SQL exécutent les mêmes opérations sur les tables de base et produisent les mêmes résultats :

/* SELECT referencing the EmployeeName view. */
SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate
FROM AdventureWorks.Sales.SalesOrderHeader AS soh
JOIN AdventureWorks.dbo.EmployeeName AS EmpN
ON (soh.ContactID = EmpN.EmployeeID)
WHERE OrderDate > '20020531';

/* SELECT referencing the Contact and Employee tables directly. */
SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate
FROM AdventureWorks.HumanResources.Employee AS e 
JOIN AdventureWorks.Sales.SalesOrderHeader AS soh
ON soh.SalesPersonID = e.EmployeeID
JOIN AdventureWorks.Person.Contact AS c
ON e.ContactID =c.ContactID
WHERE OrderDate > '20020531';

La fonctionnalité Showplan de SQL Server Management Studio montre que le moteur relationnel crée le même plan d'exécution pour ces deux instructions SELECT.

Utilisation d'indicateurs avec les vues

Les indicateurs placés sur une vue dans une requête peuvent être en conflit avec d'autres indicateurs découverts lors du développement de la vue pour l'accès à ses tables de base. Dans ce cas, la requête renvoie une erreur. Imaginons par exemple la vue suivante, dont la définition contient un indicateur de table :

USE AdventureWorks;
GO
CREATE VIEW Person.AddrState WITH SCHEMABINDING AS
SELECT a.AddressID, a.AddressLine1, 
    s.StateProvinceCode, s.CountryRegionCode
FROM Person.Address a WITH (NOLOCK), Person.StateProvince s
WHERE a.StateProvinceID = s.StateProvinceID;

Supposons à présent cette requête :

SELECT AddressID, AddressLine1, StateProvinceCode, CountryRegionCode
FROM Person.AddrState WITH (SERIALIZABLE)
WHERE StateProvinceCode = 'WA';

La requête échoue, car l'indicateur SERIALIZABLE appliqué à la vue Person.AddrState de la requête est propagé dans les tables Person.Address et Person.StateProvince de la vue lors du développement de cette dernière. Cependant, le développement de la vue révèle également l'indicateur NOLOCK sur Person.Address. La requête résultante est incorrecte parce que les indicateurs SERIALIZABLE et NOLOCK sont en conflit.

Les indicateurs de table PAGLOCK, NOLOCK, ROWLOCK, TABLOCK et TABLOCKX sont en conflit les uns avec les autres, de même que les indicateurs de table HOLDLOCK, NOLOCK, READCOMMITTED, REPEATABLEREAD et SERIALIZABLE.

Les indicateurs peuvent se propager à différents niveaux des vues imbriquées. Imaginons par exemple une requête qui applique l'indicateur HOLDLOCK sur une vue v1. Lorsque v1 est développé, il est établit que la vue v2 fait partie de sa définition. La définition de v2 inclut un indicateur NOLOCK sur l'une de ses tables de base. Cependant, cette table hérite également de l'indicateur HOLDLOCK de la requête sur la vue v1. La requête échoue parce que les indicateurs HOLDLOCK et NOLOCK sont en conflit.

Si l'indicateur FORCE ORDER est utilisé dans une requête contenant une vue, l'ordre de jointure des tables de la vue est déterminé par la position de la vue dans la construction ordonnée. Par exemple, la requête suivante effectue une sélection dans trois tables et une vue :

SELECT * FROM Table1, Table2, View1, Table3
WHERE Table1.Col1 = Table2.Col1 
    AND Table2.Col1 = View1.Col1
    AND View1.Col2 = Table3.Col2;
OPTION (FORCE ORDER)

View1 est définie comme suit :

CREATE VIEW View1 AS
SELECT Colx, Coly FROM TableA, TableB
WHERE TableA.ColZ = TableB.Colz;

L'ordre de jointure dans le plan de requête est Table1, Table2, TableA, TableB, Table3.