FORCESEEK 테이블 힌트 사용

FORCESEEK 테이블 힌트는 쿼리 최적화 프로그램이 Index Seek 연산만 쿼리에 참조된 테이블이나 뷰의 데이터에 대한 액세스 경로로 사용하도록 합니다. 이 테이블 힌트를 사용하면 쿼리 최적화 프로그램이 선택한 기본 계획을 덮어써 비효율적인 쿼리 계획으로 인한 성능 문제를 방지할 수 있습니다. 예를 들어 계획에 Table Scan 또는 Index Scan 연산자가 포함되어 있으며 해당 테이블로 인해 쿼리 실행 중 많은 읽기가 발생(STATISTICS IO 출력에서 확인 가능)하는 경우 Index Seek 연산을 수행하면 쿼리 성능이 향상될 수 있습니다. 정확하지 않은 카디널리티 또는 비용 예측으로 인해 최적화 프로그램이 계획 컴파일 시간에 Scan 연산을 우선하는 경우에 특히 그렇습니다.

FORCESEEK은 Clustered Index Seek 연산과 Nonclustered Index Seek 연산 모두에 적용됩니다. SELECT 문의 FROM 절 및 UPDATE 또는 DELETE 문의 FROM <table_source> 절에서 모든 테이블 또는 뷰에 대해 FORCESEEK을 지정할 수 있습니다.

주의 사항주의

SQL Server 쿼리 최적화 프로그램은 일반적으로 쿼리에 대해 최적의 실행 계획을 선택하므로 힌트는 숙련된 개발자나 데이터베이스 관리자가 최후의 수단으로만 사용하는 것이 좋습니다.

쿼리 계획의 FORCESEEK 적용 타당성 평가

FORCESEEK 테이블 힌트는 쿼리 계획의 테이블 또는 뷰에 Table Scan 또는 Index Scan 연산자가 사용되는 경우 유용할 수 있지만 Index Seek 연산자가 보다 효율적일 수 있습니다. 다음 쿼리 및 후속 실행 계획을 살펴보십시오.

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d 
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

다음 실행 계획에서는 두 테이블의 데이터에 액세스하기 위해 Clustered Index Scan 연산자를 선택하는 쿼리 최적화 프로그램을 보여 줍니다.

clustered index scan 연산자를 포함하는 실행 계획

FORCESEEK 힌트를 다음 쿼리와 같이 지정하여 Sales.SalesOrderDetail 테이블에 대해 쿼리 최적화 프로그램이 Seek 연산을 수행하도록 할 수 있습니다.

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d WITH (FORCESEEK)
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

다음 실행 계획에서는 쿼리에 FORCESEEK 힌트를 사용할 경우 발생하는 결과를 보여 줍니다. Sales.SalesOrderDetail 테이블의 데이터에 액세스하기 위해 Clustered Index Seek 연산이 사용됩니다.

Clustered index seek 연산자를 포함하는 실행 계획

인덱스 통합 및 교차 지원

FORCESEEK 힌트는 인덱스 통합 및 교차를 지원합니다. 이 힌트를 사용하면 쿼리 최적화 프로그램이 이러한 기술을 사용할 가능성이 높아집니다. 단순 쿼리의 컴파일 시간이 지연되지 않도록 인덱스 통합 및 교차는 일반적으로 열의 카디널리티 및 선택도를 고려하는 규칙에 따라서만 선택됩니다. 그러나 FORCESEEK 힌트를 지정하면 해당 규칙이 무시되고 이러한 기술이 항상 고려됩니다. 예를 들어 다음 쿼리를 살펴보십시오.

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 AND T.b = 2;

T 테이블의 a 및 b 열에 별도의 비클러스터형 인덱스가 있는 경우 인덱스 교차 계획이 선택될 수 있습니다. 즉, 계획에 a 열에 대한 하나의 Nonclustered Index Seek 연산과 b 열에 대한 또 다른 Nonclustered Index Seek 연산이 포함되어 있으면 이 계획은 기본 테이블에 대해 Lookup 연산을 수행하기 전에 결과 인덱스 키 집합을 교차합니다.

다음 예에서는 인덱스 통합 계획이 선택됩니다. 즉, 계획에 a 열에 대한 하나의 Seek 연산과 b 열에 대한 또 다른 Seek 연산이 포함되어 있으면 이 계획은 기본 테이블에 대해 Lookup 연산을 수행하기 전에 결과 인덱스 키 집합을 통합합니다.

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 OR T.b = 2;

LIKE 또는 IN을 사용하는 쿼리에 FORCESEEK 사용

쿼리에 IN 또는 LIKE가 검색 조건자로 사용되는 경우 쿼리 최적화 프로그램 규칙 및 잘못된 카디널리티 예측으로 인해 최적화 프로그램이 Index Seek 대신 Table Scan 또는 Index Scan 연산을 수행할 수도 있습니다.

다음 예에서는 LIKE 또는 IN이 검색 조건자로 사용되는 경우 FORCESEEK 힌트를 사용하여 쿼리 최적화 프로그램이 Table Scan 대신 Index Seek 연산을 수행하도록 하는 방법을 보여 줍니다. 쿼리 실행 계획을 보려면 예를 실행하기 전에 실제 실행 계획 포함 도구 모음 단추를 클릭합니다.

USE tempdb;
GO
DROP TABLE t;
GO
CREATE TABLE t(i int UNIQUE, j int, vc varchar(100));
CREATE INDEX t_vc ON t(vc);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WITH (FORCESEEK) WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
SELECT * FROM t WHERE vc LIKE 'Test%';
GO
SELECT * FROM t WITH (FORCESEEK) WHERE vc LIKE 'Test%';
GO
DECLARE @vc varchar(100);
SELECT * FROM t WHERE vc LIKE @vc;
GO
DECLARE @vc varchar(100);
SELECT * FROM t WITH (FORCESEEK) where vc like @vc;
GO

뷰에 FORCESEEK 사용

FORCESEEK은 인덱스 힌트와 함께 또는 인덱스 힌트 없이 지정할 수 있습니다. 뷰 또는 인덱싱된 뷰에 FORCESEEK 테이블 힌트를 적용하면 뷰의 확장된 버전에 있는 모든 테이블에 FORCESEEK 힌트가 재귀적으로 전파됩니다. 지정된 경우 인덱스 힌트는 무시됩니다. 기본 테이블에 테이블마다 하나 이상의 인덱스가 지정되어 있지 않으면 계획이 검색되지 않으며 오류 8622가 반환됩니다.

인덱싱된 뷰에 대한 참조에 FORCESEEK 힌트와 NOEXPAND 힌트를 함께 사용하면 인덱싱된 뷰가 먼저 확장되지 않고 사용됩니다. FORCESEEK 힌트는 테이블과 같이 처리되는 인덱싱된 뷰에 직접 적용됩니다.

테이블 참조에 FORCESEEK 힌트를 적용하면 테이블 참조가 인덱싱된 뷰 일치에 참여할 수 없습니다. 그러나 FORCESEEK 힌트의 영향을 받지 않는 쿼리의 다른 부분은 인덱싱된 뷰 일치에 참여할 수 있습니다. 이는 INDEX 힌트와 함께 사용할 시의 인덱싱된 뷰 일치 동작과 유사합니다.

최선의 구현 방법 고려 사항

다음과 같은 최선의 구현 방법을 권장합니다.

  • FORCESEEK 테이블 힌트를 사용하기 전에 데이터베이스의 통계가 현재 통계이며 정확한지 확인합니다.

    최적화 프로그램에서는 최신 통계를 통해 다양한 쿼리 계획의 비용을 평가하고 우수한 계획을 선택할 수 있습니다. 따라서 모든 사용자 데이터베이스에 대해 AUTO_CREATE_STATISTICS 및 AUTO_UPDATE_STATISTICS를 ON(기본값)으로 설정하는 것이 좋습니다. 또는 UPDATE STATISTICS 문을 사용하여 테이블이나 뷰의 통계를 수동으로 업데이트할 수 있습니다.

  • 잘못된 카디널리티 또는 비용 예측값을 반환할 수 있는 항목이 있는지에 대해 쿼리를 평가하고 가능한 경우 해당 항목을 제거합니다. 예를 들어 로컬 변수를 매개 변수 또는 리터럴로 바꾸고 쿼리에서 다중 문 테이블 반환 함수 및 테이블 변수의 사용을 제한합니다. 찾을 다른 항목에 대한 자세한 내용은 Microsoft SQL Sever 2005에서 쿼리 최적화 프로그램에 사용되는 통계(Statistics Used by the Query Optimizer in Microsoft SQL Server 2005)를 참조하십시오.

  • INDEX 힌트를 FORCESEEK와 함께 불필요하게 사용하지 않습니다. 즉, FORCESEEK만 사용해도 충분한 계획이 생성되는 경우 INDEX 힌트를 사용하면 최적화 프로그램의 선택 사항을 과도하게 제한할 수 있습니다. 또한 테이블의 물리적 스키마를 변경하여 힌트에 지정된 인덱스를 제거하는 경우 INDEX 힌트로 인해 쿼리가 실패할 수 있습니다. 반대로 FORCESEEK 힌트가 적용된 테이블에 사용 가능한 인덱스가 하나 이상 있는 동안은 인덱스 구조를 변경하더라도 쿼리가 컴파일됩니다.

  • INDEX 힌트 INDEX (0)을 FORCESEEK 힌트와 함께 사용하지 않습니다. INDEX (0)은 기본 테이블의 검색을 강제로 실행합니다. FORCESEEK와 함께 사용하면 계획이 검색되지 않으며 오류 8622가 반환됩니다.

  • USE PLAN 쿼리 힌트를 FORCESEEK 힌트와 함께 사용하지 않습니다. 함께 사용하면 FORCESEEK 힌트가 무시됩니다.