인덱싱된 뷰 디자인

뷰에서 반환한 결과 집합은 열과 행으로 구성된 테이블과 동일한 일반적인 형식을 갖기 때문에 뷰를 가상 테이블이라고도 하며 SQL 문에서 테이블을 참조하는 것과 동일한 방식으로 뷰를 참조할 수 있습니다. 표준 뷰의 결과 집합은 데이터베이스에 영구적으로 저장되지 않습니다. 쿼리가 표준 뷰를 참조할 때마다 SQL Server에서는 기본 테이블만 참조하는 수정된 쿼리가 구성될 때까지 내부적으로 뷰의 정의를 쿼리로 대체합니다. 그런 다음 결과 쿼리를 정상적으로 실행합니다. 자세한 내용은 뷰 확인을 참조하십시오.

표준 뷰에서 대량의 데이터를 집계하고 여러 행을 조인하는 것처럼 많은 행을 처리하는 뷰인 경우에는 뷰를 참조하는 각 쿼리마다 결과 집합을 동적으로 작성하는 데 요구되는 오버헤드가 상당히 클 수 있습니다. 쿼리에서 그러한 뷰가 자주 참조될 경우 뷰에 고유 클러스터형 인덱스를 만들면 성능이 향상될 수 있습니다. 뷰에 고유 클러스터형 인덱스를 만들면 클러스터형 인덱스가 있는 테이블의 저장 방식과 마찬가지로 결과 집합이 데이터베이스에 저장됩니다.

뷰에 인덱스를 만들어서 얻을 수 있는 또 다른 이점은 FROM 절에 뷰의 이름을 직접 지정하지 않는 쿼리에서 최적화 프로그램이 뷰 인덱스 사용을 시작할 수 있다는 것입니다. 기존 쿼리를 다시 코딩하지 않고도 더 효율적으로 인덱싱된 뷰에서 데이터를 검색할 수 있습니다. 자세한 내용은 뷰의 인덱스 확인을 참조하십시오.

기본 테이블의 데이터가 수정되면 인덱싱된 뷰에 저장된 데이터에도 수정 내용이 적용됩니다. 뷰의 클러스터형 인덱스는 고유하기 때문에 SQL Server에서는 인덱스에서 데이터 수정에 영향을 받는 행을 더 효율적으로 찾을 수 있습니다.

쿼리 최적화 프로그램은 쿼리와 뷰 정의에 모두 다음과 같은 요소가 있는 경우 쿼리를 처리할 때 이전 버전에 비해 더 효율적으로 인덱싱된 뷰를 사용할 수 있습니다.

  • 스칼라 식. 예를 들어 쿼리 최적화 프로그램에서는 스칼라 식이 조건자로 사용된 다음 쿼리를

    SELECT ColA, ColB FROM TableT WHERE ColC * (ColD + 10) > 50
    

    다음 뷰에 생성된 인덱스에 대응시킬 수 있습니다.

    CREATE VIEW V1 WITH SCHEMABINDING AS
    SELECT ColA, ColB, ColC * (ColD + 10) AS ExpCol
    FROM dbo.TableT 
    

    사용자 정의 함수가 포함된 스칼라 식도 이와 비슷한 방법으로 대응시킬 수 있습니다.

  • 스칼라 집계 함수. 예를 들어 SELECT 목록에 스칼라 집계 함수가 포함된 다음 쿼리를

    SELECT COUNT_BIG (*) FROM dbo.TableT
    

    다음 뷰에 생성된 인덱스에 대응시킬 수 있습니다.

    CREATE VIEW V2 WITH SCHEMABINDING AS
    SELECT COUNT_BIG (*) AS Cnt 
    FROM dbo.TableT 
    

또한 쿼리 최적화 프로그램에서는 다음 사항을 고려하여 쿼리 계획을 선택합니다.

  • 쿼리 조건자에 정의되어 있는 값의 간격이 인덱싱된 뷰에 정의되어 있는 간격 내에 있는지 여부. 예를 들어 다음 뷰에 생성된 인덱스를 확인합니다.

    CREATE VIEW V1 WITH SCHEMABINDING AS
    SELECT ColA, ColB, ColC FROM dbo.TableT
    WHERE ColA > 1 and ColA < 10
    

    그리고 다음 쿼리도 확인합니다.

    SELECT ColB, ColC FROM dbo.TableT
    WHERE ColA > 3 and ColA < 7
    

    쿼리에 정의되어 있는 3과 7 간의 간격이 인덱싱된 뷰에 정의되어 있는 1과 10 간의 간격 내에 있으므로 쿼리 최적화 프로그램에서는 이 쿼리를 뷰 V1에 대응시킵니다.

  • 쿼리에 정의된 식은 인덱싱된 뷰에 정의된 식에 대응합니다. SQL Server에서는 식의 열 참조, 리터럴, 논리 연산자(AND, OR, NOT, BETWEEN 및 IN) 및 비교 연산자(=, <>, >, <, >= 및 <=)를 기준으로 식을 대응시킵니다. 반면 + 및 % 등의 산술 연산자와 매개 변수는 고려하지 않습니다.

    예를 들어 쿼리 최적화 프로그램에서는 다음 쿼리를

    SELECT ColA, ColB from dbo.TableT
    WHERE ColA < ColB 
    

    다음 뷰에 생성된 인덱스에 대응시킵니다.

    CREATE VIEW V1 WITH SCHEMABINDING AS
    SELECT ColA, ColB FROM dbo.TableT
    WHERE ColB > ColA 
    

다른 모든 인덱스의 경우와 마찬가지로 SQL Server에서는 쿼리 최적화 프로그램에서 쿼리 계획에 인덱싱된 뷰를 사용하는 것이 효과적이라고 판단한 경우에만 인덱싱된 뷰를 사용합니다.

모든 버전의 SQL Server 2008에서 인덱싱된 뷰를 만들 수 있습니다. SQL Server 2008 Enterprise의 경우 쿼리 최적화 프로그램에서 인덱싱된 뷰를 자동으로 고려합니다. 다른 모든 버전에서 인덱싱된 뷰를 사용하려면 NOEXPAND 테이블 힌트를 사용해야 합니다.

인덱싱된 뷰 디자인 지침

인덱싱된 뷰는 기본 데이터가 자주 업데이트되지 않을 때 최적으로 실행됩니다. 인덱싱된 뷰의 유지 관리 비용은 테이블 인덱스의 유지 관리 비용보다 더 클 수 있습니다. 기본 데이터가 자주 업데이트되는 환경에서는 인덱싱된 뷰 데이터의 유지 관리 비용이 인덱싱된 뷰 사용에 따른 성능상의 이점에도 불구하고 부담이 될 수 있습니다. 기본 데이터가 일괄 처리를 통해 주기적으로 업데이트되지만 업데이트를 수행할 때 외에는 주로 읽기 전용으로 처리된다면 업데이트 전에 인덱싱된 뷰를 모두 삭제하고 업데이트 후 다시 작성하는 것이 좋습니다. 이렇게 하면 업데이트 성능이 향상될 수 있습니다.

인덱싱된 뷰를 사용하면 다음과 같은 쿼리 유형의 성능이 향상됩니다.

  • 많은 행을 처리하는 조인 및 집계

  • 여러 쿼리에서 자주 수행하는 조인 및 집계 작업

    예를 들어 재고를 기록하는 OLTP(온라인 트랜잭션 처리) 데이터베이스에 사용되는 쿼리에서는 ProductMaster, ProductMasterProductMaster 테이블을 조인하는 경우가 많습니다. 조인을 수행하는 각 쿼리에 따라 처리되는 행은 많지 않을 수도 있지만 이러한 유형의 쿼리는 매우 많이 사용되기 때문에 전반적인 조인 처리 성능은 중요할 수 있습니다. 이러한 관계는 자주 업데이트되지 않기 때문에 조인된 결과를 저장하는 인덱싱된 뷰를 정의하면 시스템 전체의 전반적인 성능이 향상될 수 있습니다.

  • 의사 결정 지원 작업

    분석 시스템은 자주 업데이트되지 않는 요약된 집계 데이터를 저장합니다. 데이터를 추가로 집계하고 많은 행을 조인하는 것이 여러 의사 결정 지원 쿼리의 특징입니다. 또한 의사 결정 지원 시스템에는 열 수가 많거나 열 크기가 크거나 두 가지 모두 해당하는 넓은 테이블이 포함되는 경우도 있습니다. 이러한 열의 좁은 하위 집합을 참조하는 쿼리에서는 해당 쿼리에서 참조하는 열만 포함하거나 해당 열의 좁은 상위 집합을 포함하는 인덱싱된 뷰를 사용함으로써 성능상의 이점을 얻을 수 있습니다. 단일 테이블 열의 하위 집합이 포함된 좁은 인덱싱된 뷰를 만들면 테이블이 세로로 분할되기 때문에 이러한 방식을 수직 분할 전략이라 합니다. 예를 들어 다음 테이블과 인덱싱된 뷰를 살펴봅니다.

    CREATE TABLE wide_tbl(a int PRIMARY KEY, b int, ..., z int)
    CREATE VIEW v_abc WITH SCHEMABINDING AS
    SELECT a, b, c
    FROM dbo.wide_tbl
    WHERE a BETWEEN 0 AND 1000
    CREATE UNIQUE CLUSTERED INDEX i_abc ON v_abc(a)
    

    다음 쿼리는 v_abc를 사용하여 응답할 수 있습니다.

    SELECT b, count_big(*), SUM(c)
    FROM wide_tbl 
    WHERE a BETWEEN 0 AND 1000
    GROUP BY b
    

    v_abc 뷰는 wide_tbl 테이블보다 훨씬 적은 수의 페이지를 사용합니다. 따라서 최적화 프로그램에서는 이 뷰를 이전 쿼리 해결을 위한 액세스 경로로 선택하는 것이 더 효과적일 수 있습니다.

    테이블의 하위 집합이 아니라 전체 테이블을 세로로 분할하려면 인덱싱된 뷰 대신 INCLUDE 절을 사용하여 원하는 열만 포함하는 비클러스터형 인덱스를 사용하는 것이 좋습니다. 자세한 내용은 CREATE INDEX(Transact-SQL)를 참조하십시오.

인덱싱된 뷰는 일반적으로 다음과 같은 쿼리 유형의 성능을 향상시키지는 못합니다.

  • 쓰기 작업이 잦은 OLTP 시스템

  • 업데이트가 많은 데이터베이스

  • 집계 또는 조인을 포함하지 않는 쿼리

  • GROUP BY 키의 카디널리티 수준이 높은 데이터의 집계. 카디널리티의 수준이 높다는 것은 키에 다른 값이 많이 있다는 것을 의미합니다. 모든 키는 다른 값을 가지므로 고유한 키는 가능한 가장 높은 수준의 카디널리티를 갖게 됩니다. 인덱싱된 뷰는 쿼리에서 액세스해야 하는 행의 수를 줄임으로써 성능을 향상시킵니다. 뷰의 결과 집합에 기본 테이블과 거의 같은 수의 행이 있다면 뷰를 사용해도 성능상의 이점은 거의 없습니다. 예를 들어 1,000개의 행이 있는 테이블에서 다음 쿼리를 실행합니다.

    SELECT PriKey, SUM(SalesCol)
    FROM ExampleTable
    GROUP BY PriKey
    

    테이블 키의 카디널리티가 100이라면 이 쿼리의 결과를 사용하여 작성한 인덱싱된 뷰에는 100개의 행만 포함됩니다. 뷰를 사용하는 쿼리에서는 평균적으로 기본 테이블에 필요한 읽기 작업의 1/10만 필요합니다. 고유 키인 경우에는 키의 카디널리티가 1000이므로 뷰 결과 집합이 1000개의 행을 반환합니다. 뷰와 ExampleTable 기본 테이블에 동일한 크기의 행이 있는 경우에는 기본 테이블에서 직접 읽는 대신 쿼리에 인덱싱된 뷰를 사용하더라도 성능상의 이점은 없습니다.

  • 확장 조인. 확장 조인은 결과 집합이 기본 테이블의 원래 데이터보다 큰 뷰입니다.

인덱싱된 뷰와 쿼리의 결합

인덱싱할 수 있는 뷰 유형은 제한되기 때문에 전체 문제를 해결하는 뷰를 디자인하지 못할 수도 있지만 여러 개의 더 작은 인덱싱된 뷰를 디자인하여 처리 속도를 향상시킬 수는 있습니다.

다음 예를 살펴봅니다.

  • 자주 실행되는 쿼리는 데이터를 한 데이터베이스에 집계하고 또 다른 데이터베이스에 데이터를 집계한 다음 그 결과를 조인합니다. 인덱싱된 뷰는 여러 데이터베이스의 테이블을 참조할 수 없기 때문에 전체 프로세스를 수행하는 단일 뷰는 디자인할 수 없습니다. 그러나 해당 데이터베이스에 대해 집계하는 인덱싱된 뷰를 각 데이터베이스에 만들 수 있습니다. 최적화 프로그램이 인덱싱된 뷰를 기존 쿼리에 대응시킬 수 있다면 기존 쿼리를 다시 코딩하지 않아도 최소한 집계의 처리 속도는 빨라집니다. 조인 처리 속도는 더 빠르지 않지만 쿼리는 인덱싱된 뷰에 저장된 집계를 사용하기 때문에 전반적으로 더 빨라집니다.

  • 자주 실행되는 쿼리는 여러 테이블의 데이터를 집계한 다음 UNION을 사용하여 결과를 결합합니다. 인덱싱된 뷰에서는 UNION을 사용할 수 없습니다. 개별 집계 작업을 각각 수행하도록 뷰를 다시 디자인할 수 있습니다. 그러면 쿼리를 다시 코딩하지 않고도 최적화 프로그램이 인덱싱된 뷰를 선택하여 쿼리 속도를 향상시킬 수 있습니다. UNION 처리는 향상되지 않지만 개별 집계 처리는 향상됩니다.

여러 작업을 만족시킬 수 있는 인덱싱된 뷰를 디자인합니다. FROM 절에 인덱싱된 뷰가 지정되지 않더라도 최적화 프로그램에서 인덱싱된 뷰를 사용할 수 있기 때문에 인덱싱된 뷰가 제대로 디자인되면 많은 쿼리의 처리 속도가 빨라집니다.

예를 들어 다음 뷰에 인덱스를 만드는 경우를 생각해 봅니다.

CREATE VIEW ExampleView WITH SCHEMABINDING
AS
SELECT GroupKey, SUM(Colx) AS SumColx, COUNT_BIG(Colx) AS CountColx
FROM MyTable
GROUP BY GroupKey

이 뷰는 뷰의 열을 직접 참조하는 쿼리뿐만 아니라 기본 테이블을 쿼리하고 SUM(Colx), COUNT_BIG(Colx), COUNT(Colx) 및 AVG(Colx) 등의 식을 포함하는 쿼리를 실행하는 데에도 사용될 수 있습니다. 이러한 모든 쿼리는 기본 테이블에 있는 모든 행을 읽는 대신 뷰에 있는 적은 수의 행을 검색하기만 하면 되기 때문에 처리 속도가 더 빠릅니다.

마찬가지로 일별로 데이터와 그룹을 집계하는 인덱싱된 뷰를 7일, 30일 또는 90일 등 1일 이상의 여러 범위에서 집계하는 쿼리에 사용할 수 있습니다.