SQL Server 쿼리 처리 아키텍처 _ SQL 문 처리 (2/4)
– 뷰(View) 확인
- Version : SQL Server 2005, 2008, 2008R2, 2012
SQL Server 쿼리 프로세서에서는 인덱싱된 뷰와 인덱싱 되지 않은 뷰가 다르게 처리 된다.
인덱싱된 뷰의 행은 테이블과 동일한 형식으로 데이터베이스에 저장 된다. 쿼리 프로세서에서 쿼리 계획에 인덱싱된 뷰를 사용하기로 결정하면 인덱싱된 뷰는 기본 테이블과 동일한 방법으로 처리 된다.
인덱싱되지 않은 뷰는 뷰의 정의만 저장되고 뷰의 행은 저장되지 않는다. 쿼리 최적화 프로그램은 인덱싱되지 않은 뷰를 참조하는 SQL문에 대해 작성하는 실행 계획에 뷰 정의의 논리를 추가 한다.
SQL Server 쿼리 최적화 프로그램에서 인덱싱된 뷰의 사용 여부를 결정하는 논리는 테이블 인덱스를 결정하는 데 사용되는 논리와 유사하다. SQL문 전체나 일부가 인덱싱된 뷰의 데이터를 포괄하고 해당 뷰의 인덱스가 저렴한 비용의 액세스 경로로 확인되면 쿼리의 이름이 이 뷰를 참조하는지 여부와 관계 없이 인덱스가 선택 된다.
SQL 문에서 인덱싱되지 않은 뷰를 참조할 경우 파서와 쿼리 최적화 프로그램은 SQL문의 원본과 뷰의 원본을 모두 분석하고 단일 실행계획을 세운다.
SQL문과 뷰의 별도 실행 계획이 있는 것은 아니다.
[뷰 생성]
USE AdventureWorks2008R2; GO CREATE VIEW EmployeeName AS SELECT h.BusinessEntityID, p.LastName, p.FirstName FROM HumanResources.Employee AS h JOIN Person.Person AS p ON h.BusinessEntityID = p.BusinessEntityID; GO |
[뷰를 사용한 실행 계획]
위에서 생성한 뷰를 기반으로 아래 스크립트에서 두 SQL문이 모두 기본 테이블에 대해 동일한 작업을 수행하고 동일한 결과를 생성한다.
/* SELECT referencing the EmployeeName view. */ SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate FROM AdventureWorks2008R2.Sales.SalesOrderHeader AS soh JOIN AdventureWorks2008R2.dbo.EmployeeName AS EmpN ON (soh.SalesPersonID = EmpN.BusinessEntityID) WHERE OrderDate > '20020531'; |
/* SELECT referencing the Person and Employee tables directly. */ SELECT LastName AS EmployeeLastName, SalesOrderID, OrderDate FROM AdventureWorks2008R2.HumanResources.Employee AS e JOIN AdventureWorks2008R2.Sales.SalesOrderHeader AS soh ON soh.SalesPersonID = e.BusinessEntityID JOIN AdventureWorks2008R2.Person.Person AS p ON e.BusinessEntityID =p.BusinessEntityID WHERE OrderDate > '20020531'; |
[뷰 힌트]
쿼리의 뷰에 힌트를 넣으면 뷰가 확장되어 기본 테이블에 액세스할 때 발견되는 다른 힌트와 서로 충돌 할 수 있다. 이경우 쿼리에서 오류를 반환 한다.
다음 예제 스크립트는 뷰에 적용된 SERIALIZABLE 힌트가 뷰 확장시 원본 테이블의 Person.Address, Person.StateProvince 테이블에 모두 전파되기 때문에 이 쿼리는 Person.Address의 LOCK 힌트와 충돌로 인하여 오류를 반환 한다.
아래 스크립트는 뷰 생성시 NOLOCK 힌트를 사용하였다.
--CREATE VIEW 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; |
아래 스크립트는 데이터 조회 시 NOLOCK이 사용하여 생성된 뷰를 SERIALIZABLE 힌트를 사용하여 조회한 결과 이다.
SELECT AddressID, AddressLine1, StateProvinceCode, CountryRegionCode FROM Person.AddrState WITH (SERIALIZABLE) WHERE StateProvinceCode = 'WA'; |
이 외에 다양한 잠금 힌트가 서로 충돌하여 오류를 발생 한다. 중첩된 뷰의 경우에도 뷰가 확장 할 때 상속 받은 뷰는 영향을 받게 된다.
[뷰 사용과 테이블 조인 순서]
뷰를 포함하는 쿼리에 FORCE ORDER 힌트를 사용하면 정렬된 구조체에서의 뷰 위치에 따라 뷰 내의 테이블 조인 순서가 결정 된다.
-- CREATE TABLE CREATE TABLE TBL_A(ID NVARCHAR(50),NAME NVARCHAR(50)) CREATE TABLE TBL_B(ID NVARCHAR(50),NAME NVARCHAR(50)) CREATE TABLE TBL_C(ID NVARCHAR(50),NAME NVARCHAR(50)) CREATE TABLE TBL_D(ID NVARCHAR(50),NAME NVARCHAR(50)) CREATE TABLE TBL_E(ID NVARCHAR(50),NAME NVARCHAR(50)) CREATE TABLE TBL_F(ID NVARCHAR(50),NAME NVARCHAR(50)) GO
INSERT INTO TBL_A VALUES ('KANG SW', 'HTTP://SQLMVP.KR') INSERT INTO TBL_B VALUES ('KANG SW', 'HTTP://SQLMVP.KR') INSERT INTO TBL_C VALUES ('KANG SW', 'HTTP://SQLMVP.KR') INSERT INTO TBL_D VALUES ('KANG SW', 'HTTP://SQLMVP.KR') INSERT INTO TBL_E VALUES ('KANG SW', 'HTTP://SQLMVP.KR') INSERT INTO TBL_F VALUES ('KANG SW', 'HTTP://SQLMVP.KR') GO
-- CREATE VIEW CREATE VIEW VIEW1 AS SELECT C.ID, D.NAME FROM TBL_C AS C INNER JOIN TBL_D AS D ON C.ID = D.ID |
- 실행 계획 표시 : http://sqlmvp.kr/140181977090
실행 결과 쿼리 계획에서 조인 순서는 TBL_A, TBL_B, VIEW1, TBL_E, TBL_F로 실행 된 것을 확인 할 수 있다.
[참고 자료]
http://msdn.microsoft.com/ko-kr/library/ms190237(v=sql.105).aspx
http://msdn.microsoft.com/ko-kr/library/ms187864(v=sql.105).aspx
강성욱 / jevida@naver.com
Microsoft SQL Server MVP
Blog : http://sqlmvp.kr
Facebook : http://facebook.com/sqlmvp