다음을 통해 공유


뷰 확인

SQL Server 쿼리 프로세서에서는 인덱싱된 뷰와 인덱싱되지 않은 뷰가 다르게 처리됩니다.

  • 인덱싱된 뷰의 행은 테이블과 동일한 형식으로 데이터베이스에 저장됩니다. 쿼리 프로세서에서 쿼리 계획에 인덱싱된 뷰를 사용하기로 결정하면 인덱싱된 뷰는 기본 테이블과 동일한 방법으로 처리됩니다.

  • 인덱싱되지 않은 뷰의 정의만 저장되고 뷰의 행은 저장되지 않습니다. 쿼리 최적화 프로그램은 인덱싱되지 않은 뷰를 참조하는 SQL 문에 대해 작성하는 실행 계획에 뷰 정의의 논리를 추가합니다.

SQL Server 쿼리 최적화 프로그램에서 인덱싱된 뷰의 사용 시기를 결정하는 데 사용되는 논리는 테이블 인덱스의 사용 시기를 결정하는 데 사용되는 논리와 유사합니다. SQL 문의 전체나 일부가 인덱싱된 뷰의 데이터를 포괄하고 해당 뷰의 인덱스가 저렴한 비용의 액세스 경로로 확인되면 쿼리의 이름이 이 뷰를 참조하는지 여부와 관계없이 인덱스가 선택됩니다. 자세한 내용은 뷰의 인덱스 확인을 참조하십시오.

SQL 문에서 인덱싱되지 않은 뷰를 참조할 경우 파서와 쿼리 최적화 프로그램은 SQL 문의 원본과 뷰의 원본을 모두 분석하고 단일 실행 계획을 세웁니다. SQL 문과 뷰에 대해 별도의 계획이 있는 것은 아닙니다.

예를 들어 다음과 같은 뷰가 있습니다.

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

이 뷰를 기반으로 두 SQL 문이 모두 기본 테이블에 대해 동일한 작업을 수행하고 동일한 결과를 생성합니다.

/* 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';

SQL Server Management Studio 실행 계획 기능을 통해 관계형 엔진이 두 SELECT 문에 대해 동일한 실행 계획을 세우는 것을 확인할 수 있습니다.

뷰에 힌트 사용

쿼리의 뷰에 힌트를 넣으면 뷰가 확장되어 기본 테이블에 액세스할 때 발견되는 다른 힌트와 서로 충돌할 수 있습니다. 이러한 경우 쿼리에서 오류를 반환합니다. 예를 들어 다음과 같이 뷰 정의에 테이블 힌트가 포함되어 있습니다.

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;

다음 쿼리를 입력한다고 가정합니다.

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

쿼리의 Person.AddrState 뷰에 적용되는 SERIALIZABLE 힌트가 뷰 확장 시 뷰의 Person.Address 테이블과 Person.StateProvince 테이블에 모두 전파되기 때문에 이 쿼리는 실패합니다. 그러나 뷰가 확장될 때 Person.Address의 NOLOCK 힌트도 나타납니다. SERIALIZABLE 힌트와 NOLOCK 힌트가 충돌하기 때문에 결과 쿼리가 올바르지 않습니다.

PAGLOCK, NOLOCK, ROWLOCK, TABLOCK 또는 TABLOCKX 테이블 힌트는 서로 충돌하며 HOLDLOCK, NOLOCK, READCOMMITTED, REPEATABLEREAD 및 SERIALIZABLE 테이블 힌트도 마찬가지입니다.

여러 수준의 중첩된 뷰를 통해 힌트가 전파될 수 있습니다. 예를 들어 v1 뷰에 HOLDLOCK 힌트를 적용하는 쿼리가 있다고 가정합니다. v1이 확장될 때 이 뷰의 정의에 v2 뷰가 포함되어 있음을 확인했습니다. v2의 정의에는 이 뷰의 기본 테이블 중 하나에 대한 NOLOCK 힌트가 있습니다. 그러나 이 테이블에는 v1 뷰의 쿼리로부터 HOLDLOCK 힌트도 상속됩니다. NOLOCK 힌트와 HOLDLOCK 힌트가 충돌하므로 쿼리가 실패합니다.

뷰를 포함하는 쿼리에 FORCE ORDER 힌트를 사용하면 정렬된 구조체에서의 뷰 위치에 따라 뷰 내의 테이블 조인 순서가 결정됩니다. 예를 들어 다음 쿼리는 세 개의 테이블과 한 개의 뷰에서 선택합니다.

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은 다음과 같이 정의됩니다.

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

쿼리 계획의 조인 순서는 Table1, Table2, TableA, TableB, Table3입니다.