반응형
반응형

쿼리 힌트는 해당 힌트가 전체 쿼리에 사용해야 하는 힌트임을 나타냅니다. 쿼리 힌트는 문의 모든 연산자에 영향을 줍니다. 기본 쿼리에 UNION이 포함된 경우 UNION 연산과 연관된 마지막 쿼리에만 OPTION 절을 포함할 수 있습니다. 쿼리 힌트는 OPTION 절의 일부로 지정됩니다. 하나 이상의 쿼리 힌트로 인해 쿼리 최적화 프로그램에서 유효한 계획을 생성할 수 없는 경우 8622 오류가 발생합니다.

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

적용 대상

DELETE

INSERT

SELECT

UPDATE

MERGE

항목 링크 아이콘 Transact-SQL 구문 표기 규칙

구문

<query_hint > ::= 
{ { HASH | ORDER } GROUP 
  | { CONCAT | HASH | MERGE } UNION 
  | { LOOP | MERGE | HASH } JOIN 
  | FAST number_rows 
  | FORCE ORDER 
  | MAXDOP number_of_processors 
  | OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )
  | OPTIMIZE FOR UNKNOWN
  | PARAMETERIZATION { SIMPLE | FORCED }
  | RECOMPILE
  | ROBUST PLAN 
  | KEEP PLAN 
  | KEEPFIXED PLAN
  | EXPAND VIEWS 
  | MAXRECURSION number 
  | USE PLAN N'xml_plan'
    | TABLE HINT ( exposed_object_name  [ , <table_hint> [ [, ]...n ] ] )
<table_hint> ::=
[ NOEXPAND ] { 
    INDEX ( index_value [ ,...n ] ) | INDEX = ( index_value )
  | FASTFIRSTROW 
  | FORCESEEK
  | HOLDLOCK 
  | NOLOCK 
  | NOWAIT
  | PAGLOCK 
  | READCOMMITTED 
  | READCOMMITTEDLOCK 
  | READPAST 
  | READUNCOMMITTED 
  | REPEATABLEREAD 
  | ROWLOCK 
  | SERIALIZABLE 
  | TABLOCK 
  | TABLOCKX 
  | UPDLOCK 
  | XLOCK 
}

인수

{ HASH | ORDER } GROUP

쿼리의 GROUP BY, DISTINCT 또는 COMPUTE 절에 지정된 집계에서 해시나 정렬을 사용하도록 지정합니다.

{ MERGE | HASH | CONCAT } UNION

UNION 집합을 병합, 해시 또는 연결하여 모든 UNION 연산을 수행하도록 지정합니다. 둘 이상의 UNION 힌트를 지정한 경우 쿼리 최적화 프로그램에서는 지정한 힌트 중 가장 부담이 적은 전략을 선택합니다.

{ LOOP | MERGE | HASH } JOIN

전체 쿼리에서 모든 조인 연산이 LOOP JOIN, MERGE JOIN 또는 HASH JOIN에 의해 수행되도록 지정합니다. 둘 이상의 조인 힌트를 지정한 경우 최적화 프로그램에서는 허용되는 힌트 중 가장 부담이 적은 조인 방법을 선택합니다.

같은 쿼리에서 특정 테이블 쌍에 대해 FROM 절에 조인 힌트도 지정한 경우 두 테이블의 조인에서 조인 힌트가 우선적으로 적용되지만 쿼리 힌트도 고려됩니다. 따라서 테이블 쌍에 대한 조인 힌트는 쿼리 힌트에서 허용되는 조인 방법의 선택만 제한하게 될 수도 있습니다. 자세한 내용은 조인 힌트(Transact-SQL)를 참조하십시오.

FAST number_rows

첫 번째 음수가 아닌 정수 number_rows를 빨리 검색하기 위해 쿼리를 최적화하도록 지정합니다. 첫 번째 number_rows를 반환한 후에 쿼리는 계속 실행하여 전체 결과 집합을 만듭니다.

FORCE ORDER

쿼리 구문에 지정된 조인 순서가 쿼리 최적화 시 유지되도록 지정합니다. FORCE ORDER를 사용해도 쿼리 최적화 프로그램이 취할 수 있는 역할 반전 동작에는 영향을 미치지 않습니다. 자세한 내용은 해시 조인 이해를 참조하십시오.

참고:
MERGE 문에서 WHEN SOURCE NOT MATCHED 절이 지정되어 있지 않으면 원본 테이블은 기본 조인 순서에 따라 대상 테이블보다 먼저 액세스됩니다. FORCE ORDER를 지정하면 이러한 기본 동작이 유지됩니다.

쿼리에 뷰가 포함된 경우 SQL Server 쿼리 최적화 프로그램이 FORCE ORDER 힌트를 강제 적용하는 방법은 뷰 확인을 참조하십시오.

MAXDOP number

이 옵션을 지정한 쿼리에 대해서만 sp_configuremax degree of parallelism 구성 옵션을 무시합니다. sp_configure로 구성한 값을 초과하면 MAXDOP 쿼리 힌트는 효과가 없습니다. MAXDOP 쿼리 힌트를 사용할 때 max degree of parallelism 구성 옵션에 사용된 모든 의미 체계 규칙은 적용할 수 있습니다. 자세한 내용은 max degree of parallelism 옵션을 참조하십시오.

OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )

쿼리가 컴파일되고 최적화될 때 쿼리 최적화 프로그램이 지역 변수에 대해 특정 값을 사용하도록 지시합니다. 해당 값은 쿼리 최적화 중에만 사용되고 쿼리 실행 중에는 사용되지 않습니다.

@variable_name

쿼리에서 사용된 지역 변수의 이름이며 OPTIMIZE FOR 쿼리 힌트와 함께 사용하도록 값을 할당할 수 있습니다.

UNKNOWN

쿼리 최적화 프로그램이 쿼리 최적화 동안 초기 값 대신 통계 데이터를 사용하여 지역 변수 값을 결정하도록 지정합니다.

literal_constant

OPTIMIZE FOR 쿼리 힌트와 함께 사용하도록 @variable_name에 할당할 리터럴 상수 값입니다. literal_constant는 쿼리 최적화 중에만 사용되며 쿼리 실행 중에는 @variable_name의 값으로 사용되지 않습니다. literal_constant는 리터럴 상수로 표현할 수 있는 모든 SQL Server 시스템 데이터 형식이 될 수 있습니다. literal_constant의 데이터 형식은 쿼리에서 @variable_name이 참조하는 데이터 형식으로 암시적으로 변환될 수 있어야 합니다.

OPTIMIZE FOR는 최적화 프로그램의 기본 매개 변수 감지 동작을 무효로 만들 수 있으며 계획 지침을 만들 때 사용할 수 있습니다. 자세한 내용은 저장 프로시저 다시 컴파일계획 지침을 사용하여 배포된 응용 프로그램의 쿼리 최적화를 참조하십시오.

OPTIMIZE FOR UNKNOWN

쿼리가 컴파일 및 최적화될 때 쿼리 최적화 프로그램이 강제 매개 변수화를 통해 만든 매개 변수를 비롯한 모든 지역 변수에 대해 초기 값 대신 통계 데이터를 사용하도록 지시합니다. 강제 매개 변수화에 대한 자세한 내용은 강제 매개 변수화를 참조하십시오.

같은 쿼리 힌트에서 OPTIMIZE FOR @variable_name = literal_constant 및 OPTIMIZE FOR UNKNOWN을 사용하면 쿼리 최적화 프로그램이 특정 값에는 지정된 literal_constant를 사용하고 나머지 변수 값에는 UNKNOWN을 사용합니다. 해당 값은 쿼리 최적화 중에만 사용되고 쿼리 실행 중에는 사용되지 않습니다.

PARAMETERIZATION { SIMPLE | FORCED }

SQL Server 쿼리 최적화 프로그램에서 쿼리 컴파일 시 적용하는 매개 변수화 규칙을 지정합니다.

중요:
PARAMETERIZATION 쿼리 힌트는 계획 지침 내에서만 지정할 수 있습니다. 쿼리 내에서 직접 지정할 수는 없습니다.

SIMPLE은 쿼리 최적화 프로그램이 단순 매개 변수화를 시도하도록 지시합니다. FORCED는 최적화 프로그램이 강제 매개 변수화를 시도하도록 지시합니다. PARAMETERIZATION 쿼리 힌트는 계획 지침 내에서 PARAMETERIZATION 데이터베이스 SET 옵션의 현재 설정을 무시하는 데 사용됩니다. 자세한 내용은 계획 지침을 사용하여 쿼리 매개 변수화 동작 지정을 참조하십시오.

RECOMPILE

쿼리를 실행한 후 SQL Server 데이터베이스 엔진에서 해당 쿼리에 대해 생성된 계획을 삭제하도록 하여 다음에 같은 쿼리가 실행될 때 쿼리 최적화 프로그램이 쿼리 계획을 다시 컴파일하도록 지시합니다. RECOMPILE을 지정하지 않으면 데이터베이스 엔진은 쿼리 계획을 캐시하여 다시 사용합니다. 쿼리 계획을 컴파일할 때 RECOMPILE 쿼리 힌트는 쿼리에 있는 지역 변수의 현재 값을 사용하며 쿼리가 저장 프로시저 안에 있는 경우 매개 변수에 전달된 현재 값을 사용합니다.

RECOMPILE은 전체 저장 프로시저가 아닌 저장 프로시저 내 쿼리의 하위 집합만 다시 컴파일해야 하는 경우 WITH RECOMPILE 절을 사용하는 저장 프로시저를 만드는 데 유용합니다. 자세한 내용은 저장 프로시저 다시 컴파일을 참조하십시오. RECOMPILE은 계획 지침을 만들 때도 유용합니다. 자세한 내용은 계획 지침을 사용하여 배포된 응용 프로그램의 쿼리 최적화를 참조하십시오.

ROBUST PLAN

쿼리 최적화 프로그램에서 성능이 저하되더라도 최대 잠재적 행 크기를 정의할 수 있는 계획을 세우도록 합니다. 쿼리가 처리될 때 중간 테이블 및 연산자가 입력 행보다 큰 행을 저장하고 처리해야 할 수 있습니다. 행이 너무 커서 특정 연산자가 행을 처리하지 못하는 경우도 있습니다. 이런 경우 데이터베이스 엔진에서는 쿼리 실행 중에 오류를 생성합니다. ROBUST PLAN을 사용하면 쿼리 최적화 프로그램에서 이러한 문제가 발생할 수 있는 쿼리 계획을 고려하지 않도록 할 수 있습니다.

이러한 계획이 불가능할 경우 쿼리 최적화 프로그램은 쿼리 실행 시 오류를 검색하도록 지연시키지 않고 오류를 반환합니다. 행에는 가변 길이 열이 포함될 수 있으며 데이터베이스 엔진에서 처리할 수 없는 최대 잠재적 크기를 가진 행을 정의할 수 있습니다. 그러나 대개 응용 프로그램은 데이터베이스 엔진이 처리할 수 있는 한도 내의 실제 크기를 가진 행을 저장합니다. 길이가 너무 긴 행이 있으면 데이터베이스 엔진에서 실행 오류를 반환합니다.

KEEP PLAN

쿼리 최적화 프로그램에서 쿼리에 대한 예상 다시 컴파일 임계값을 완화하도록 합니다. 예상 다시 컴파일 임계값은 테이블에 UPDATE, DELETE, MERGE 또는 INSERT 문을 실행하여 인덱싱된 열을 예상 수만큼 변경했을 때 쿼리가 자동으로 다시 컴파일되는 시점입니다. 테이블이 자주 업데이트되는 경우 KEEP PLAN을 지정하면 쿼리가 지나치게 자주 다시 컴파일되지 않아 유용합니다.

KEEPFIXED PLAN

통계 변경 시에는 최적화 프로그램이 쿼리를 다시 컴파일하지 않도록 합니다. KEEPFIXED PLAN을 지정하면 원본으로 사용하는 테이블의 스키마가 바뀌거나 테이블에 대해 sp_recompile이 실행되는 경우에만 쿼리를 다시 컴파일합니다.

EXPAND VIEWS

인덱싱된 뷰를 확장하고 쿼리 최적화 프로그램에서 인덱싱된 뷰를 쿼리 일부를 대체하는 것으로 간주하지 않도록 지정합니다. 쿼리 텍스트에 있는 뷰 정의에 의해 뷰 이름이 바뀌면 뷰가 확장됩니다.

이 쿼리 힌트는 쿼리 계획에서 인덱싱된 뷰와 인덱싱된 뷰의 인덱스를 직접 사용하도록 허용하지 않습니다.

인덱싱된 뷰는 쿼리의 SELECT 부분에서 뷰를 직접 참조하고 WITH (NOEXPAND) 또는 WITH (NOEXPAND, INDEX( index_value [ ,...n ] ) )가 지정된 경우에만 확장되지 않습니다. 쿼리 힌트 WITH (NOEXPAND)에 대한 자세한 내용은 FROM을 참조하십시오.

INSERT, UPDATE, MERGE 및 DELETE 문을 비롯한 문의 SELECT 부분에 있는 뷰만 힌트의 영향을 받습니다.

MAXRECURSION number

해당 쿼리에 대해 허용되는 최대 재귀 횟수를 지정합니다. number는 0에서 32767 사이의 음수가 아닌 정수입니다. 0을 지정하면 한도가 적용되지 않습니다. 이 옵션을 지정하지 않은 경우 서버에 대한 기본 한도는 100입니다.

쿼리 실행 중에 MAXRECURSION 한도로 지정된 횟수 또는 기본 횟수에 도달하면 쿼리가 종료되고 오류가 반환됩니다.

이 오류로 인해 문의 모든 결과가 롤백됩니다. 문이 SELECT 문인 경우 결과의 일부가 반환되거나 아무런 결과도 반환되지 않을 수 있습니다. 반환된 일부 결과에는 지정한 최대 재귀 수준을 초과한 재귀 수준의 모든 행이 포함되지 않을 수 있습니다.

자세한 내용은 WITH common_table_expression(Transact-SQL)을 참조하십시오.

USE PLAN N'xml_plan'

쿼리 최적화 프로그램이 'xml_plan'에 의해 지정된 쿼리에 대해 기존의 쿼리 계획을 사용하도록 합니다. 자세한 내용은 계획 강제 적용을 사용하여 쿼리 계획 지정을 참조하십시오. USE PLAN은 INSERT, UPDATE, MERGE 또는 DELETE 문에서 지정할 수 없습니다.

TABLE HINT ( exposed_object_name [ , <table_hint> [ [, ]...n ] ] )

지정된 테이블 힌트를 exposed_object_name에 해당하는 테이블 또는 뷰에 적용합니다. 테이블 힌트는 계획 지침의 컨텍스트에서 쿼리 힌트로만 사용하는 것이 좋습니다.

exposed_object_name은 다음 참조 중 하나일 수 있습니다.

  • 쿼리의 FROM 절에서 테이블 또는 뷰에 별칭을 사용하는 경우 exposed_object_name은 해당 별칭입니다.

  • 별칭을 사용하지 않는 경우 exposed_object_name은 FROM 절에서 참조되는 테이블 또는 뷰와 일치합니다. 예를 들어 테이블 또는 뷰가 두 부분으로 된 이름을 사용하여 참조되는 경우 exposed_object_name도 똑같이 두 부분으로 된 이름입니다.

테이블 힌트는 지정하지 않고 exposed_object_name만 지정하면 쿼리에 개체에 대한 테이블 힌트의 일부로 지정된 인덱스가 모두 무시되고 쿼리 최적화 프로그램에서 인덱스 사용 여부를 결정합니다. 이 방법은 원래 쿼리를 수정할 수 없을 때 INDEX 테이블 힌트의 효과를 제거하는 데 이용할 수 있습니다. 자세한 내용은 예 10을 참조하십시오.

<table_hint> ::= { [ NOEXPAND ] { INDEX ( index_value [ ,...n ] ) | INDEX = ( index_value ) | FASTFIRSTROW | FORCESEEK | HOLDLOCK | NOLOCK | NOWAIT | PAGLOCK | READCOMMITTED | READCOMMITTEDLOCK | READPAST | READUNCOMMITTED | REPEATABLEREAD | ROWLOCK | SERIALIZABLE | TABLOCK | TABLOCKX | UPDLOCK | XLOCK }

exposed_object_name에 해당하는 테이블 또는 뷰에 쿼리 힌트로 적용할 테이블 힌트입니다. 이러한 힌트에 대한 자세한 내용은 테이블 힌트(Transact-SQL)를 참조하십시오.

쿼리에 테이블 힌트를 지정하는 WITH 절이 없다면 INDEX 및 FORCESEEK 이외의 테이블 힌트를 쿼리 힌트로 사용할 수 없습니다. 자세한 내용은 주의를 참조하십시오.

주의

쿼리 힌트는 문 내에 SELECT 절이 사용되는 경우를 제외하고 INSERT 문에서 지정할 수 없습니다.

쿼리 힌트는 하위 쿼리가 아닌 최상위 쿼리에서만 지정할 수 있습니다. 테이블 힌트를 쿼리 힌트로 지정하면 해당 힌트를 최상위 쿼리나 하위 쿼리에 지정할 수 있지만 TABLE HINT 절에서 exposed_object_name에 대해 지정한 값이 쿼리 또는 하위 쿼리의 표시 이름과 일치해야 합니다.

매개 변수가 있는 쿼리와 함께 다시 컴파일을 사용한 성능 향상

SQL Server 2008 쿼리 최적화 프로그램은 RECOMPILE 쿼리 힌트를 사용하는 쿼리를 컴파일할 때 상수의 논리를 평가 및 단순화하여 쿼리 실행 계획의 효율성을 향상시키려고 합니다. 컴파일 시 실제 매개 변수 값이 알려져 쿼리 최적화 프로그램이 상수의 논리를 평가하기 전에 매개 변수를 상수로 대체할 수 있기 때문에 매개 변수가 있는 쿼리는 이러한 향상 내용을 활용할 수 있습니다.

예를 들어 다음 WHERE 절은 선택적 매개 변수가 많이 포함된 검색 쿼리의 일부입니다.

WHERE (o.OrderID = @orderid OR @orderid IS NULL)

AND (o.OrderDate >= @fromdate OR @fromdate IS NULL)

AND (o.OrderDate <= @todate OR @todate IS NULL)

AND (od.UnitPrice >= @minprice OR @minprice IS NULL)

AND (od.UnitPrice <= @maxprice OR @maxprice IS NULL)

AND (o.CustomerID = @custid OR @custid IS NULL)

AND (c.CompanyName LIKE @custname + '%' OR @custname IS NULL)

AND (c.City = @city OR @city IS NULL)

AND (c.Region = @region OR @region IS NULL)

AND (c.Country = @country OR @country IS NULL)

AND (od.ProductID = @prodid OR @prodid IS NULL)

AND (p.ProductName LIKE @prodname + '%' OR @prodname IS NULL)

쿼리가 RECOMPILE 쿼리 힌트를 사용하여 실행될 때 쿼리 최적화 프로그램은 각 매개 변수의 값에 따라 NULL 매개 변수가 있는 조건자를 제거하여 WHERE 절의 논리를 단순화합니다. 이 검색 쿼리가 @minprice = $5, @prodname = 'The'를 사용하여 호출되고 다른 모든 매개 변수가 NULL일 경우 쿼리 계획은 다음 조건자를 사용합니다.

WHERE od.UnitPrice >= $5 AND p.ProductName LIKE 'The%'.

WHERE 절이 간단할수록 쿼리 최적화 프로그램은 Index Scan 대신 Index Seek를 선택하는 것과 같이 보다 효율적인 쿼리 계획을 선택할 수 있습니다. 이전 버전의 SQL Server에서는 대부분의 매개 변수가 NULL인 경우에도 단순하지 않은 WHERE 절의 논리를 모두 평가하는 작업이 쿼리 계획에 포함되기 때문에 쿼리 최적화 프로그램이 이 쿼리에 대해 Index Scan을 선택할 가능성이 높습니다.

참고:
RECOMPILE 쿼리 힌트를 사용한 매개 변수 대체는 커서에 대한 DML 문에는 적용되지 않습니다.

이러한 성능 향상으로 인해 RECOMPILE 쿼리 힌트를 사용하는 쿼리가 이전 버전의 SQL Server에서는 성공적으로 컴파일되고 실행된 경우에도 SQL Server 2008에서는 실행 시 컴파일 오류가 발생할 수 있습니다. 예를 들어 다음 일괄 처리에는 매개 변수가 있는 쿼리를 사용하는 저장 프로시저가 포함되어 있습니다. 이전 버전의 SQL Server에서는 이 일괄 처리가 성공적으로 컴파일되고 실행됩니다. SQL Server 2008에서는 이 쿼리를 처음 호출하면 성공적으로 컴파일되지만 두 번째로 호출하면 오류 8622와 함께 컴파일이 실패합니다. 컴파일 오류가 발생하는 것은 쿼리 최적화 프로그램이 매개 변수를 상수로 대체한 다음 쿼리 논리를 단순화하여 HASH JOIN 힌트를 사용할 수 없기 때문입니다.

코드 복사
USE AdventureWorks;
GO
IF OBJECT_ID ( 'HumanResources.uspGetAddressesByAddressIDs', 'P' ) IS NOT NULL
DROP PROCEDURE HumanResources.uspGetAddressesByAddressIDs;
GO
-- Return a join of Person.Address and HumanResources.EmployeeAddress
-- on the AddressID column. If the two AddressID parameters are not 
-- equal, return a FULL JOIN. If the two AddressID parameters are equal,
-- return an INNER JOIN where Person.Address.AddressID and 
-- HumanResources.EmployeeAddress.AddressID equal the parameter values.
CREATE PROCEDURE HumanResources.uspGetAddressesByAddressIDs 
    @AddressID1 int, @AddressID2 int
AS
SELECT * FROM Person.Address JOIN HumanResources.EmployeeAddress
    ON ( (Person.Address.AddressID = HumanResources.EmployeeAddress.AddressID)
    AND ( Person.Address.AddressID=@AddressID1
    OR @AddressID1 != @AddressID2 ) )
OPTION (HASH JOIN, RECOMPILE);
GO
-- Parameters are not equal. Query compiles with no errors and 
-- returns all rows in the JOIN.
EXEC HumanResources.uspGetAddressesByAddressIDs 163,20;
GO
-- AddressID's do not match. Logic simplifies to finding
-- Person.Address.AddressID=163 and 
-- HumanResources.EmployeeAddress.AddressID=163.
-- The HASH JOIN query hint is no longer applicable and the query
-- gets error 8622 during compilation.
EXEC HumanResources.uspGetAddressesByAddressIDs 163,163;
GO

테이블 힌트를 쿼리 힌트로 지정

INDEX 또는 FORCESEEK 테이블 힌트는 계획 지침의 컨텍스트에서 쿼리 힌트로만 사용하는 것이 좋습니다. 계획 지침은 타사 응용 프로그램인 경우와 같이 원래 쿼리를 수정할 수 없을 때 유용합니다. 계획 지침에 지정되어 있는 쿼리 힌트는 쿼리가 컴파일 및 최적화되기 전에 쿼리에 추가됩니다. 임시 쿼리의 경우 계획 지침 문을 테스트할 때에만 TABLE HINT 절을 사용하십시오. 임시 쿼리 이외의 모든 경우 이러한 힌트를 테이블 힌트로만 지정하는 것이 좋습니다.

INDEX 및 FORCESEEK 테이블 힌트를 쿼리 힌트로 지정하는 경우 다음 개체에 대해서 유효합니다.

  • 테이블



  • 인덱싱된 뷰

  • 공통 테이블 식(공통 테이블 식을 채울 결과 집합을 위한 SELECT 문에 힌트를 지정해야 함)

  • 동적 관리 뷰

  • 명명된 하위 쿼리

기존 테이블 힌트가 없는 쿼리에 대해 INDEX 및 FORCESEEK 테이블 힌트를 쿼리 힌트로 지정할 수 있습니다. 또는 INDEX 및 FORCESEEK 힌트를 사용하여 쿼리에 있는 기존 INDEX 또는 FORCESEEK 힌트를 각각 대체할 수 있습니다. 쿼리에 테이블 힌트를 지정하는 WITH 절이 없다면 INDEX 및 FORCESEEK 이외의 테이블 힌트를 쿼리 힌트로 사용할 수 없습니다. 이 경우 OPTION 절에 TABLE HINT를 사용하여 일치하는 힌트를 쿼리 힌트로 지정함으로써 쿼리의 의미 체계를 유지해야 합니다. 예를 들어 쿼리에 테이블 힌트 NOLOCK이 있는 경우 계획 지침의 @hints 매개 변수에 있는 OPTION 절에도 NOLOCK 힌트가 있어야 합니다. 예 11을 참조하십시오. 일치하는 쿼리 힌트 없이 OPTION 절에 TABLE HINT를 사용하여 INDEX 또는 FORCESEEK 이외의 테이블 힌트를 지정하거나 그 반대의 경우 OPTION 절로 인해 쿼리의 의미 체계가 변경되고 쿼리가 실패할 수 있음을 나타내는 오류 8702가 발생합니다. 자세한 내용은 계획 지침에서 INDEX 및 FORCESEEK 쿼리 힌트 사용을 참조하십시오.

1. MERGE JOIN 사용

다음 예에서는 쿼리의 JOIN 연산이 MERGE JOIN에 의해 수행되도록 지정합니다.

코드 복사
USE AdventureWorks;
GO
SELECT * 
FROM Sales.Customer AS c
INNER JOIN Sales.CustomerAddress AS ca ON c.CustomerID = ca.CustomerID
WHERE TerritoryID = 5
OPTION (MERGE JOIN);
GO

2. OPTIMIZE FOR 사용

다음 예에서는 쿼리를 최적화할 때 쿼리 최적화 프로그램이 지역 변수 @city_name'Seattle' 값을 사용하고 통계 데이터를 사용하여 지역 변수 @postal_code의 값을 결정하도록 지시합니다.

코드 복사
USE AdventureWorks;
GO
DECLARE @city_name nvarchar(30);
DECLARE @postal_code nvarchar(15);
SET @city_name = 'Ascheim';
SET @postal_code = 86171;
SELECT * FROM Person.Address
WHERE City = @city_name AND PostalCode = @postal_code
OPTION ( OPTIMIZE FOR (@city_name = 'Seattle', @postal_code UNKNOWN) );
GO

3. MAXRECURSION 사용

잘못 구성된 재귀 공통 테이블 식이 무한 루프에 진입하는 것을 방지하는 데 MAXRECURSION을 사용할 수 있습니다. 다음 예에서는 의도적으로 무한 루프를 만들고 MAXRECURSION 힌트를 사용하여 재귀 수준을 2로 제한하는 방법을 보여 줍니다.

코드 복사
USE AdventureWorks;
GO
--Creates an infinite loop
WITH cte (EmployeeID, ManagerID, Title) AS
(
    SELECT EmployeeID, ManagerID, Title
    FROM HumanResources.Employee
    WHERE ManagerID IS NOT NULL
  UNION ALL
    SELECT cte.EmployeeID, cte.ManagerID, cte.Title
    FROM cte 
    JOIN  HumanResources.Employee AS e 
        ON cte.ManagerID = e.EmployeeID
)
--Uses MAXRECURSION to limit the recursive levels to 2
SELECT EmployeeID, ManagerID, Title
FROM cte
OPTION (MAXRECURSION 2);
GO

코딩 오류를 교정한 다음에는 더 이상 MAXRECURSION이 필요하지 않습니다.

4. MERGE UNION 사용

다음 예에서는 MERGE UNION 쿼리 힌트를 사용합니다.

코드 복사
USE AdventureWorks ;
GO
SELECT *
FROM HumanResources.Employee e1
UNION
SELECT *
FROM HumanResources.Employee e2
OPTION (MERGE UNION) ;
GO

5. HASH GROUP 및 FAST 사용

다음 예에서는 HASH GROUPFAST 쿼리 힌트를 사용합니다.

코드 복사
USE AdventureWorks ;
GO
SELECT ProductID, OrderQty, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (HASH GROUP, FAST 10) ;
GO

6. MAXDOP 사용

다음 예에서는 MAXDOP 쿼리 힌트를 사용합니다.

코드 복사
USE AdventureWorks ;
GO
SELECT ProductID, OrderQty, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (MAXDOP 2);
GO

7. INDEX 사용

다음은 INDEX 힌트를 사용하는 예입니다. 첫 번째는 단일 인덱스를 지정하는 예이고, 두 번째는 단일 테이블 참조에 대해 여러 인덱스를 지정하는 예입니다. 두 예에서 INDEX 힌트는 별칭을 사용하는 테이블에 적용되므로 TABLE HINT 절에서도 표시된 개체 이름과 동일한 별칭을 지정해야 합니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide1', 
    @stmt = N'SELECT c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e 
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 2;', 
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT(e, INDEX (IX_Employee_ManagerID)))';
GO
EXEC sp_create_plan_guide 
    @name = N'Guide2', 
    @stmt = N'SELECT c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 2;', 
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT(e, INDEX(PK_Employee_EmployeeID, IX_Employee_ManagerID)))';
GO

8. FORCESEEK 사용

다음은 FORCESEEK 테이블 힌트를 사용하는 예입니다. 이 예에서 INDEX 힌트는 두 부분으로 된 이름을 사용하는 테이블에 적용되므로 TABLE HINT 절에서도 표시된 개체 이름과 동일한 두 부분으로 된 이름을 지정해야 합니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide3', 
    @stmt = N'SELECT c.LastName, c.FirstName, HumanResources.Employee.Title
              FROM HumanResources.Employee
              JOIN Person.Contact AS c ON HumanResources.Employee.ContactID = c.ContactID
              WHERE HumanResources.Employee.ManagerID = 3
              ORDER BY c.LastName, c.FirstName;', 
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT( HumanResources.Employee, FORCESEEK))';
GO

9. 여러 테이블 힌트 사용

다음 예에서는 한 테이블에 INDEX 힌트를 적용하고 다른 테이블에 FORCESEEK 힌트를 적용합니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide4', 
    @stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e 
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 3;', 
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID ) ) 
                       , TABLE HINT ( c, FORCESEEK) )';
GO

10. TABLE HINT를 사용하여 기존 테이블 힌트 다시 정의

다음은 힌트를 지정하지 않고 TABLE HINT 힌트를 사용하여 쿼리의 FROM 절에 지정된 INDEX 테이블의 동작을 다시 정의하는 방법을 보여 주는 예입니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide5', 
    @stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e WITH (INDEX (IX_Employee_ManagerID))
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 3;', 
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT(e))';
GO

11. 의미 체계에 영향을 주는 테이블 힌트 지정

다음 예의 쿼리에는 두 가지 테이블 힌트가 포함되어 있습니다. 하나는 의미 체계에 영향을 주는 NOLOCK이고 다른 하나는 의미 체계에 영향을 주지 않는 INDEX입니다. 쿼리의 의미 체계를 유지하기 위해 계획 지침의 OPTIONS 절에 NOLOCK 힌트가 지정됩니다. NOLOCK 힌트 외에 INDEX 및 FORCESEEK 힌트가 지정되고 문을 컴파일 및 최적화할 때 쿼리에서 의미 체계에 영향을 주지 않는 INDEX 힌트를 대체합니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide6', 
    @stmt = N'SELECT c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e 
                   WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 3;',
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID) , NOLOCK, FORCESEEK ))';
GO

다음 예에서는 최적화 프로그램에서 테이블 힌트에 지정된 인덱스 이외의 인덱스를 선택할 수 있도록 하면서 쿼리의 의미 체계를 유지하는 다른 방법을 보여 줍니다. 이 작업은 의미 체계에 영향을 주는 NOLOCK 힌트를 OPTIONS 절에 지정하고 테이블 참조만 있고 INDEX 힌트는 없는 TABLE HINT 키워드를 지정하여 수행됩니다.

코드 복사
USE AdventureWorks;
GO
EXEC sp_create_plan_guide 
    @name = N'Guide7', 
    @stmt = N'SELECT c.LastName, c.FirstName, e.Title
              FROM HumanResources.Employee AS e 
                   WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
              JOIN Person.Contact AS c ON e.ContactID = c.ContactID
              WHERE e.ManagerID = 2;',
    @type = N'SQL',
    @module_or_batch = NULL, 
    @params = NULL, 
    @hints = N'OPTION (TABLE HINT ( e, NOLOCK))';
GO

참고 항목

참조

힌트(Transact-SQL)
sp_create_plan_guide(Transact-SQL)
sp_control_plan_guide(Transact-SQL)

관련 자료

계획 지침에서 INDEX 및 FORCESEEK 쿼리 힌트 사용

도움말 및 정보

SQL Server 2008 지원 받기
반응형
반응형

앞에서 sys.dm_db_index_usage_stats 동적 관리 뷰에 대하여 설명하였습니다. sys.dm_db_index_usage_stats 동적 관리 뷰 외에 sys.dm_db_index_operational_stats 라는 동적 관리 함수가 있습니다.
sys.dm_db_index_operational_stats
현재 데이터베이스 또는 지정한 데이터베이스에 있는 테이블 또는 인덱스의 파티션별 I/O, 잠금, 래치 및 액세스 작업 방법에 대한 정보를 제공합니다.

sys.dm_db_index_operational_stats를 사용하여 사용자가 테이블, 인덱스 또는 파티션을 읽거나 쓰기 위해 대기해야 하는 시간을 추적하고 상당한 I/O 작업 또는 문제가 발생하고 있는 테이블이나 인덱스를 식별할 수 있습니다. 즉,
이러한 열을 사용하여 경합이 발생하고 있는 영역을 식별할 수 있습니다.

 

 

[구문] sys.dm_db_index_operational_stats (

    { database_id | NULL }

    , { object_id | NULL }

    , { index_id | NULL | 0 }

    , { partition_number | NULL }

)

 

Adventureworks 데이터베이스내의 모든 인덱스에 대한 정보 확인하기

SELECT *

FROM sys.dm_db_index_operational_stats(db_id('adventureworks'),null,null,null);

GO

 

SQL Server 인스턴스 내의 모든 테이블 및 인덱스에 대한 정보 확인하기

SELECT * FROM sys.dm_db_index_operational_stats(NULL, NULL, NULL, NULL);
GO

 

 

AdventureWorks 데이터베이스에 있는 특정 테이블에 대한 정보 반환하기

SET @db_id = DB_ID(N'AdventureWorks');
SET @object_id = OBJECT_ID(N'AdventureWorks.Person.Address');
SELECT * FROM sys.dm_db_index_operational_stats(@db_id, @object_id, NULL, NULL);
GO

 

반응형
반응형

실제 많은 수의 운영 환경들에서 테이블에 만들어져 있는 인덱스들의 일부는 실제로는 사용되지 않는 인덱스입니다. 실제로 사용되지 않는 인덱스를 관리하는 것은 불필요한 오버헤드만 유발하므로 인덱스 사용여부를 확인하고 실제로 사용되지 않는 불필요한 인덱스들을 구분해 내고 삭제하는 것이 필요합니다.
SQL Server 2005에서 새롭게 제공되는 DMV 중에서 sys.dm_db_index_usage_stats
동적 관리 뷰를 사용하여 사용된 인덱스와 빈도를 있습니다. 뷰를 사용하여 응용 프로그램에서 사용하지 않는 인덱스를 확인할 있으며, 또한 유지 관리 오버헤드를 유발하는 인덱스를 확인할 수도 있습니다.

SQL Server 서비스를 시작할 때마다 카운터는 상태로 초기화되며, 데이터베이스가 분리되거나 종료될 때마다(: AUTO_CLOSE ON으로 설정된 경우) 데이터베이스와 관련된 모든 행이 제거됩니다. 그러므로 DMV에 대한 스냅샷을 영구 테이블에 복사해 둠으로써 SQL Server가 재시작되기 이전의 데이터도 관리하는 것을 권고합니다.

 

다음에 sys.dm_db_index_usage_stats 동적 관리 뷰를 활용한 예제 스크립트가 있습니다. DMV를 활용한 스크립트는 아래 예제 스크립트 외에도 다양하게 작성될 수 있습니다.

Adventureworks 데이터베이스 인덱스 사용현황 확인하기

SELECT * FROM sys.dm_db_index_usage_stats

WHERE database_id = db_id('Adventureworks');

GO

 

사용되지 않은 인덱스 확인하기

SELECT object_name(i.object_id) AS Table_Name, i.name AS Index_Name

FROM sys.indexes i, sys.objects o

WHERE  i.index_id NOT IN

             (SELECT s.index_id

              FROM sys.dm_db_index_usage_stats s

              WHERE s.object_id=i.object_id and

                                       i.index_id=s.index_id and

                                        database_id = db_id() )  -- dbid : db_id() 값을 입력

AND o.type = 'U'

AND o.object_id = i.object_id

ORDER BY object_name(i.object_id) ASC

GO


[주의] 인덱스가 사용되지 않는 인덱스라고 확인되더라도 충분히 오랜 기간 동안 모니터링하지 않았다면 섣불리 인덱스를 삭제해도 무방하다고 판단해서는 안됩니다. 만일 크기가 큰 테이블에 대해서 어떤 응용 프로그램에서 사용하는 인덱스를 실수로 삭제하게 되면 심각한 블로킹과 성능 저하가 발생할 수 있습니다.

반응형
반응형

불필요한 인덱스를 삭제하지 않고 그대로 두면 오버헤드만 유발하므로, 인덱스가 불필요하다고 판단되면 삭제하는 것이 바람직합니다.

인덱스를 삭제하고자 하는 경우에는 DROP INDEX를 사용하면 됩니다.
DROP INDEX 구문도 SQL Server 2005에서 변경되었으므로 DROP INDEX를 사용하기 전에 SQL Server 2005 온라인 설명서에서 DROP INDEX 구문을 살펴 보시기 바랍니다.

이전 버전과의 호환성을 위해 SQL Server 2000의 구문이 지원되기는 하지만 이후 버전에서는 제거될 수 있으므로 SQL Server 2005 구문에 맞게 작성 또는 수정하실 것을 권고합니다.

-- SQL Server 2000 구문

DROP INDEX Employee.IX_Employee_ManagerID;

GO

-- SQL Server 2005 구문
DROP INDEX IX_Employee_ManagerID ON Employee;

GO

인덱스 관련 T-SQL 문 중에 ALTER INDEX 문이 있는데, ALTER INDEX로는 인덱스를 구성하는 열을 변경하거나 파일 그룹을 변경할 없습니다. 인덱스를 구성하는 열을 추가 또는 삭제, 순서를 변경하거나 인덱스가 저장된 파일 그룹을 변경하고자 하는 경우에는 인덱스를 삭제한 다시 생성해야 합니다.

 
만일 PRIMARY KEY 제약 조건이나 UNIQUE 제약 조건을 정의함으로써 생성된 인덱스는 DROP INDEX로 삭제할 수 없습니다.
이러한 제약 조건들은 ALTER TABLE 문의 DROP CONSTRAINT 절을 사용하여 삭제합니다.

반응형
반응형

인덱스 비활성화는 사용자가 인덱스를 사용할 없도록 하며, 제약 조건을 포함한 모든 인덱스에 사용 가능합니다. , 비활성화된 인덱스는 유지 관리 되지 않고, 쿼리 옵티마이저에 의해 고려 되지도 않습니다. 비클러스터형 인덱스 또는 클러스터형 인덱스를 비활성화하면 인덱스의 메타 데이터는 시스템 카탈로그에 남기지만, 물리적으로 인덱스 데이터를 삭제합니다. 그러므로, 비활성화된 인덱스와 동일한 이름으로 인덱스를 생성할 없습니다. 클러스터형 인덱스의 경우는 비활성화하면 데이터는 삭제되지 않고 남아 있지만, 해당 테이블의 데이터에 접근할 없어, 데이터의 수정은 물론 조회도 불가능합니다.

인덱스의 비활성화 여부는 sys.indexes 카탈로그 뷰의 is_disabled 열을 확인합니다. 인덱스의 비활성화는 ALTER INDEX DISABLE 사용하고, 비활성화된 인덱스를 다시 활성화 시키기 위해서는 ALTER INDEX REBUILD 또는 CREATE INDEX WITH DROP_EXISTING 구문을 사용합니다. 비활성화된 클러스터형 인덱스를 재작성할 때에는 ONLINE 옵션을 ON으로 설정할 없으며, 비활성화된 비클러스터형 인덱스를 재작성할 때에는 ONLINE 옵션을 ON으로 설정할 있습니다.

 

비클러스터형 인덱스 비활성화한 , 다시 활성화하기

USE AdventureWorks;

GO

-- 비클러스터형 인덱스 비활성화

ALTER INDEX IX_Employee_ManagerID ON HumanResources.Employee

DISABLE ;

GO

-- 비활성화된 인덱스 확인

SELECT is_disabled FROM sys.indexes

WHERE object_id=object_id('HumanResources.Employee')

AND name='IX_Employee_ManagerID';

/* 1 */

-- 온라인으로 인덱스 재작성

ALTER INDEX IX_Employee_ManagerID ON HumanResources.Employee

REBUILD WITH (ONLINE=ON);

GO

-- 재작성된 인덱스의 비활성화 여부 확인

SELECT is_disabled FROM sys.indexes

WHERE object_id=object_id('HumanResources.Employee')

AND name='IX_Employee_ManagerID';

GO

 

클러스터형 인덱스 비활성화한 , 다시 활성화하기

USE AdventureWorks;

GO

ALTER INDEX PK_Employee_EmployeeID ON HumanResources.Employee

DISABLE ;

GO

SELECT * FROM HumanResources.Employee;

GO

/*

메시지 8655, 수준 16, 상태 1, 1

테이블 또는 Employee' 인덱스 'PK_Employee_EmployeeID'() 비활성화되었으므로 쿼리 프로세서에서 계획을 생성할 없습니다.

*/

ALTER INDEX ALL ON HumanResources.Employee

REBUILD;

GO

 

 

FOREIGN KEY 제약 조건이 있는 클러스터형 인덱스 비활성화한 , 활성화하기

USE Sample;

GO

-- 테스트 테이블 생성

CREATE TABLE STATES (STATE CHAR(2) NOT NULL);

GO

CREATE TABLE CITIES (

                            CITY VARCHAR(30) NOT NULL,

                            STATE CHAR(2) ,

                  ZIP INT);

GO

-- PK 제약 조건과 FK 제약 조건 생성

ALTER TABLE STATES

ADD CONSTRAINT PK_STATES PRIMARY KEY CLUSTERED (STATE);

GO

ALTER TABLE CITIES

ADD CONSTRAINT PK_CITIES PRIMARY KEY CLUSTERED (CITY);

GO

ALTER TABLE CITIES

ADD CONSTRAINT FK_CITIES_STATES_STATE FOREIGN KEY (STATE)

REFERENCES STATES (STATE);

GO

 

-- CITIES 테이블에 데이터 INSERT

-- STATES 테이블에 없는 PR 값은 CITIES INSERT 없다

INSERT INTO STATES SELECT 'CA';

GO

INSERT INTO CITIES SELECT 'Los Angles', 'CA',111;

GO

INSERT INTO CITIES SELECT 'San Juan', 'PR' ,222;

GO

/*

메시지 547, 수준 16, 상태 0, 1

INSERT 문이 FOREIGN KEY 제약 조건 "FK_CITIES_STATES_STATE"() 충돌했습니다. 데이터베이스 "sample", 테이블 "dbo.STATES", column 'STATE'에서 충돌이 발생했습니다.

문이 종료되었습니다.

*/

 

-- STATES 테이블의 클러스터형 인덱스 비활성화

-- FOREIGN KEY 제약 조건도 동시에 비활성화된다

ALTER INDEX PK_STATES ON STATES

DISABLE;

GO

/*

경고: 인덱스 'PK_STATES'() 비활성화한 결과 테이블 'STATES'() 참조하는 테이블 'CITIES' 외래 'FK_CITIES_STATES_STATE'() 비활성화되었습니다.

*/

 

-- CITIES 테이블에 데이터 INSERT

-- (FOREIGN KEY 제약 조건이 비활성화되었으므로 STATES 테이블에 없는 PR 값을 INSERT 있다)

INSERT INTO CITIES SELECT 'San Juan', 'PR' ,222;

GO

 

-- STATES 테이블의 클러스터형 인덱스 재작성

ALTER INDEX PK_STATES ON STATES

REBUILD;

GO

-- CITIES 테이블에 데이터 INSERT

-- (FOREIGN KEY 제약 조건은 여전히 비활성화 상태이므로 STATES 테이블에 없는 PR 값을 INSERT 있다)

INSERT INTO CITIES SELECT 'Detroit', 'PR' ,333;

GO

 

-- FOREIGN KEY 제약 조건 재생성하기

ALTER TABLE  CITIES

CHECK CONSTRAINT FK_CITIES_STATES_STATE;

-- CITIES 테이블에 데이터 INSERT

-- (FOREIGN KEY 제약 조건이 재생성되었으므로 STATES 테이블에 없는 PR 값을 INSERT 없다)

INSERT INTO CITIES SELECT 'Kenmore', 'PR' ,444;

GO

/*

메시지 547, 수준 16, 상태 0, 1

INSERT 문이 FOREIGN KEY 제약 조건 "FK_CITIES_STATES_STATE"() 충돌했습니다. 데이터베이스 "sample", 테이블 "dbo.STATES", column 'STATE'에서 충돌이 발생했습니다.

문이 종료되었습니다.

*/

 

 

[참고]

PRIMARY KEY 제약 조건을 FOREIGN KEY 제약 조건이 참조하고 있는 경우, 인덱스가 비활성화되면 FOREIGN KEY 제약 조건도 동시에 비활성화됩니다. 그러나, PRIMARY KEY 제약 조건을 재구성했다고 하더라도 FOREIGN KEY 제약 조건은 재구성되지 않으므로, PRIMARY KEY 제약 조건을 재구성한 수동으로 FOREIGN KEY 제약 조건을 재생성해야 합니다.

반응형
반응형

SQL Server 2005에서 인덱스와 관련하여 개선된 기능 중 하나인 온라인 인덱스 작업에 대하여 알아 보겠습니다. 온라인 인덱스 작업의 지원은 1년365일, 24시간 운영되어야 하는 시스템을 관리하는 DBA들이 학수고대하던 기능입니다. 온라인 인덱스 작업의 지원으로 인하여 인덱스 작업으로 인한 다운타임이 감소하고 인덱스를 재구성하고 싶어도 소요시간 때문에 인덱스 재구성 작업을 엄두도 내지 못하던 대용량 시스템에 도움이 될 것으로 생각됩니다.

SQL Server 2005에서는 온라인으로 인덱스를 만들고 다시 작성하고 삭제할 수 있게 되었습니다. ONLINE 옵션을 사용하면 인덱스 작업이 수행되는 동안 기본 테이블(BASE TABLE)이나 클러스터형 인덱스 데이터를 쿼리하고 업데이트할 수 있게 되었습니다.

ONLINE 옵션은 다음과 같은 Transact-SQL 문에서 사용할 수 있습니다.
- CREATE INDEX
- ALTER INDEX
- DROP INDEX
- ALTER TABLE (CLUSTERED 인덱스 옵션을 사용하는 UNIQUE 제약 조건이나 PRIMARY KEY 제약 조건을 추가하거나 삭제하는 경우)


참고로, 인덱스를 다시 구성하는 작업은 항상 온라인으로 수행됩니다만, 인덱스를 다시 작성하는 작업은 기본적으로 오프라인으로 수행됩니다. 인덱스를 다시 작성할 때에도 ONLINE
옵션을 ON으로 설정하면 변경 중에 아주 잠시 동안만 배타 테이블 잠금이 유지되기 때문에 인덱스 작성 중에도 데이터의 조회와 수정 가능하게 됩니다.


온라인
인덱스 작업은 SQL Server 2005 Enterprise Edition에서만 가능합니다.

반응형
반응형

앞에서 인덱스 조각화 확인 방법을 소개하였으므로, 이제는 인덱스 조각화를 제거하는 방법을 설명하겠습니다. 인덱스 조각화를 제거하는 방법에는 여러 가지가 있습니다. 인덱스를 재구성하거나 인덱스를 재작성하거나 인덱스를 삭제하고 다시 만드는 등의 여러 가지 방법이 가능합니다. SQL Server 2005에서 인덱스 조각화 제거 구문에 변경이 발생하였으므로, 이미 아시는 내용이겠지만 기본적인 내용을 살펴 보겠습니다.  

인덱스 다시 구성

 

ALTER INDEX 문에 REORGANIZE 절을 사용하면 인덱스를 다시 구성할 있습니다. SQL Server 2000에서의 DBCC INDEXDEFRAG 대신 기능을 사용하는 것을 권고합니다.

인덱스를 다시 구성하면 리프 노드의 논리적 순서(왼쪽에서 오른쪽으로) 일치하도록 리프 수준 페이지가 다시 정렬되어 테이블과 뷰의 클러스터형 비클러스터형 인덱스의 리프 수준이 조각 모음됩니다. 클러스터형 인덱스를 다시 구성하면 클러스터형 인덱스의 리프 수준에 포함된 모든 LOB 열이 압축됩니다. 비클러스터형 인덱스를 다시 구성하면 인덱스 내의 포괄 열인 모든 LOB 열이 압축됩니다.

sys.indexes 카탈로그 뷰의 채우기 비율 값을 기준으로 인덱스의 페이지를 압축하고, 압축으로 인해 생성된 페이지는 제거됩니다. 재구성은 온라인으로 수행되며, 차단 잠금을 오래 보유하지 않으므로 쿼리나 업데이트의 실행을 차단하지 않습니다.

인덱스가 심하게 조각화되지 않은 경우에는 인덱스를 재구성하면 되지만, 인덱스가 심하게 조각화된 경우에는 인덱스를 재작성하는 것이 좋습니다..

 

인덱스 재구성하기

USE AdventureWorks;

GO

ALTER INDEX PK_ProductPhoto_ProductPhotoID ON Production.ProductPhoto

REORGANIZE ;

GO

 

 

인덱스 다시 작성

 

인덱스 조각화로 인한 성능 저하를 방지하기 위해서는 주기적으로 인덱스 조각화가 진행된 테이블들에 대한 조각화 제거 작업이 필요합니다. 조각화를 제거하기 위한 작업 중의 하나가 인덱스를 다시 작성하는 것입니다. 인덱스 재작성은 인덱스를 삭제한 다시 생성하는 작업으로, 인덱스를 논리적 정렬과 일치하도록 물리적으로 재정렬합니다. 인덱스가 만들어졌을 지정된 FILLFACTOR 계산하여 인덱스의 페이지를 압축하여, 디스크 공간을 확보하고 필요한 만큼 페이지를 할당하여 인덱스 행을 연속되는 페이지에 다시 정렬합니다. 작업은 ALTER INDEX REBUILD 절을 사용하여 있으며, DBCC DBREINDEX 대신 기능을 사용하는 것을 권고합니다. 또한, CREATE INDEX DROP_EXISTING 절을 사용하여 인덱스 재작성 작업을 수도 있습니다. 작업을 통하여, 요청한 데이터를 얻는 필요한 페이지 읽기 횟수를 줄일 있으므로 디스크 성능이 향상됩니다.  ALL 명시하면 하나의 트랜잭션으로 테이블의 모든 인덱스를 제거하고 다시 작성합니다. SQL Server 2005에서는 비클러스터형 인덱스를 온라인으로 재작성할 있습니다.

가능한 인덱스 재작성 작업을 자동화하여 주기적으로 용이하게 수행할 있는 체계를 갖출 것을 권고합니다.

 

비클러스터형 인덱스를 온라인 모드로 재작성하기

ALTER INDEX IX_Employee_ManagerID ON HumanResources.Employee

REBUILD WITH (ONLINE=ON);

GO

 

반응형
반응형

인덱스 옵션 중에 IGNORE_DUP_KEY 라는 옵션이 있습니다. 이 옵션은 기본 모드가 중복 무시인 이기종 DBMS에서 SQL Server 2005로 마이그레이션하는 경우에 유용하게 사용할 수 있는 옵션이므로, 여러분에게 소개 드리고자 합니다.

IGONORE_DUP_KEY
옵션을 활성화하면 다중 INSERT 작업 중복 값이 들어올 오류가 아닌 경고 메시지를 반환하고 고유 인덱스에 위배되는 행만 INSERT에서 제외됩니다. 다른 문제만 없다면 고유 인덱스에 위배되지 않는 나머지 행들은 성공적으로 INSERT가 이루어집니다.
 
IGNORE_DUP_KEY 옵션을 비활성화한 상태에서는 고유 인덱스에 위배되는 행이 존재하면 오류 메시지가 반환되며 전체 INSERT 트랜잭션이 롤백됩니다.

참고로, 인덱스 옵션은 WITH 절을 사용하여 기술하며 SQL Server 2005의 구문은 SQL Server 2000의 구문과 조금 달라졌습니다. SQL Server 2005에서 새롭게 지원되는 인덱스 옵션은 WITH (option_name = ON}OFF) 을 사용해야만 지정할 수 있습니다. 설사 SQL Server 2000과 호환되는 옵션이더라도 새롭게 스크립트를 작성하거나 기존 스크립트를 수정하는 경우에는 SQL Server 2005의 구문을 준수하여 작성할 것을 권고합니다.

- SQL Server 2000 : WITH <index_option>
- SQL Server 2005 : WITH <index_option = ON | OFF>


다음에 IGNORE_DUP_KEY 옵션을 사용한 예제가 있습니다. 한번 따라해 보시면 쉽게 이해가 되실 겁니다.

-- 1. 테스트 테이블을 만듭니다.

CREATE TABLE #Test (C1 nvarchar(10), C2 nvarchar(50), C3 datetime);

GO

-- 2. 테스트 테이블에 IGNORE_DUP_KEY 옵션이 활성화된 인덱스를 생성합니다.

CREATE UNIQUE INDEX AK_Index ON #Test (C2)

    WITH (IGNORE_DUP_KEY = ON);

GO


-- 3. 하나의 일괄 처리(Batch) 안에서 Adventureworks.Production.UnitMeasure 테이블에 존재하는 행과 동일한 값을 가지는 한 행을 미리 INSERT 하고 Adventureworks.Production.UnitMeasure 테이블의 전체 데이터를 복사함으로써 중복  INSERT를 발생시킵니다. 오류 대신 경고 메시지가 반환되며 정상적으로 처리되는 것을 확인할 수 있습니다.
INSERT INTO #Test VALUES (N'OC', N'Ounces', GETDATE());

INSERT INTO #Test

SELECT * FROM Adventureworks.Production.UnitMeasure;

GO

/*

중복 키가 무시되었습니다.

*/

-- 클린업
DROP TABLE #Test;

GO

반응형
반응형

SQL Server 2005에는 키가 아닌 열을 포함한 비클러스터형 인덱스를 생성할 수가 있습니다. 키가 아닌 열을 포함한 인덱스, 포괄 열이 있는 인덱스는 인덱스 커버링 가능성을 높여 주기 때문에 쿼리의 성능을 개선시키는데 도움이 됩니다.
참고로 인덱스 커버링이랑
쿼리 옵티마이저가 테이블 데이터나 클러스터형 인덱스의 데이터를 참조하지 않고 비클러스터형 인덱스의 값만을 참조하여 원하는 데이터를 얻는 것을 말합니다. 이와 같이 데이터나 클러스터형 인덱스를 참조하지 않게 되면 적은 I/O로 원하는 결과를 얻게 되므로 성능적인 측면에서 도움이 됩니다.

포괄 열이 있는 인덱스는 다음과 같은 장점이 있습니다.

 

?         인덱스 열로 허용되지 않는 데이터 형식을 포괄 열에는 포함시킬 수 있습니다. 포괄 열 인덱스에는 text, ntext, image 데이터 형식을 제외한 모든 데이터 형식이 허용됩니다.

?         비클러스터형 인덱스를 구성하는 인덱스 열의 개수 또는 인덱스 키의 크기의 계산에서 제외됩니다.


다음 예제에서와 같이 CREATE INDEX 문에 INCLUDE 절을 추가하여 포괄 열 인덱스를 생성할 수 있습니다. 

[따라하기] 포괄 열을 추가한 인덱스 생성하기

USE AdventureWorks;

GO

-- 포괄 열을 추가한 인덱스 생성

CREATE NONCLUSTERED INDEX IX_Address_PostalCode

    ON Person.Address (PostalCode)

    INCLUDE (AddressLine1, AddressLine2, City, StateProvinceID);

GO

-- 포괄 인덱스로 인해 성능이 향상되는 쿼리

SELECT AddressLine1, AddressLine2, City, StateProvinceID, PostalCode

FROM Person.Address

WHERE PostalCode BETWEEN N'98000' and N'99999';

GO

 

반응형
반응형

인덱스는 테이블이나 뷰의 조회를 빠르게 하기 위한 데이터 구조입니다. 인덱스는 테이블이나 뷰의 또는 이상의 열로 구성되어 있는 키로 구성되어 있으며, 키는 B-트리 구조로 구성되어 있습니다. 인덱스는 조회의 속도는 증가 시키지만, INSERT 속도는 저하시키므로 적절한 인덱스를 생성하는 것이 중요합니다. 인덱스는 테이블, , 또는 테이블의 XML 인덱스에 생성할 있습니다.

 

인덱스의 종류

 

n       클러스터형 인덱스

클러스터형 인덱스를 구성하는 값의 순서에 의해 테이블 또는 뷰의 데이터가 물리적으로 정렬되어 있습니다. 그러므로, 클러스터형 인덱스는 테이블에 개만이 존재할 있습니다. 클러스터형 인덱스가 존재하여 클러스터형 인덱스의 값에 따라 데이터가 정렬되어 있는 테이블을 클러스터형 테이블이라고 하고, 클러스터형 인덱스가 없는 테이블을 이라고 합니다.

 

클러스터형 인덱스는 다음과 같은 선정 조건을 가지는 것이 좋습니다.

 

?         고유한 값을 가지는 열로 생성하는 것이 좋습니다. 중복값이 존재하는 경우에는 시스템 내부에서 고유한 값을 가지기 위하여 “uniqifier” 추가되기 때문에 추가적인 오버 헤드가 발생합니다.

?         인덱스 길이는 짧은 것이 좋습니다. 클러스터형 인덱스가 길어지면 비클러스터형 인덱스의 길이도 길어지기 때문에 인덱스 크기가 커집니다.

?         정적인 열에 생성하는 것이 좋습니다. 자주 변경되는 열에 클러스터형 인덱스를 생성하면 키에 대한 업데이트 작업에 대한 비용이 증가합니다.

 

[따라하기] 클러스터형 인덱스 생성하기

CREATE TABLE TestTable (a int, b int, c AS a/b);

GO

CREATE CLUSTERED INDEX IDX_c ON TestTable (c);

GO

 

n       비클러스터형 인덱스

비클러스터형 인덱스는 데이터와 분리된 데이터 구조를 가집니다. 비클러스터형 인덱스는 비클러스터형 인덱스 값을 포함하며, 값에 해당하는 데이터 열이 위치한 포인터 로케이터 가집니다. 클러스터형 인덱스를 가지고 테이블의 비클러스터형 인덱스의 경우에 로케이터는 클러스터형 인덱스 키이고, 클러스터형 인덱스가 없는 테이블의 경우에는 로케이터는 열의 위치를 나타내는 RID입니다.
반응형

+ Recent posts

반응형