SQL Server 카디널리티 힌트


14

카디널리티 평가를 SQL Server 최적화 프로그램 (모든 버전)에 '주입'하는 방법이 있습니까?

즉, 오라클의 카디널리티 힌트와 유사한 것입니다.

내 동기는 쿼리 최적화 프로그램이 얼마나 좋은가? [1] , 카디널리티 추정기의 나쁜 계획 선택에 대한 영향을 테스트합니다. 따라서 SQL Server가 복잡한 쿼리에 대한 카디널리티를 정확하게 '추정'하도록 강요하면 충분합니다.


[1] Leis, Viktor 등. "실제로 쿼리 최적화 도구가 얼마나 좋은가요?"
VLDB 엔 다우먼트 9.3 (2015) : 204-215.

답변:


10

CARDINALITY전략적으로 사용 TOP하고 MANY() Adam Machanic이 개발 한 사용자 정의 함수를 사용하여 Oracle의 힌트 와 유사한 것을 얻을 수 있습니다 . 몇 가지 예를 살펴 보겠습니다. 무료로 제공되는 AdventureWorks 데이터베이스를 사용하고 있습니다. th다음 쿼리에서 파생 테이블이 반환하는 행 수를 실제로 제어해야한다고 가정합니다 .

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID;

있는 그대로 113443 행으로 추정됩니다.

쿼리 시작

견적을 낮추 려면 쿼리 힌트 와 함께 행 목표를 설정할 th수 있습니다 . 이를 수행하는 한 가지 방법이 있습니다.TOPOPTIMIZE FOR

DECLARE @row_goal BIGINT = 9223372036854775807;
SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1));

우리는 추정치가 단 하나의 행이라는 것을 알 수 있습니다.

1 행 추정

결과가 변경되지 않도록 @row_goal가능한 가장 큰 BIGINT값으로 설정 했습니다 . OPTIMIZE FOR것처럼 쿼리 힌트는 쿼리 최적화에 최적화 지시 @row_goalIS 1로 동일 나도 같은 결과를 얻을 수 있지만, 쿼리가 다르게 최적화됩니다.

카디널리티 예상치를 늘리는 것이 더 까다 롭습니다. TOP옵티마이 저가 충분한 행을 리턴하지 않음을 인식하므로 값을 늘릴 수 없습니다 . 그러나 MANY()함수를 사용 하여 추정치에 행을 추가 할 수 있습니다 . 이 MANY()함수는 항상 0 개의 행을 반환하지만 행 추정값은 입력 매개 변수에 따라 변경됩니다. 파생 테이블에서 행 예상 값을 10X 늘려야한다고 가정하십시오. 그것을 달성하는 한 가지 방법 :

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (9223372036854775807) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON 1=1
) th ON p.ProductID = th.ProductID;

추정치가 기본 테이블의 10 배임을 알 수 있습니다.

10X 쿼리

TOP옵티마이 저가 테이블을 이동하지 못하도록 불필요한 기능 이 추가되었습니다. 그것 없이는 MANY()기능이 계획의 잘못된 장소에 적용될 수 있습니다.

행 수에 계수를 곱하는 대신 정확한 과대 평가를 원할 경우 두 기술을 결합 할 수 있습니다. 예를 들어, 파생 테이블의 추정치가 정확히 1000000 행이어야한다고 가정하십시오. 그것을 달성하는 한 가지 방법 :

DECLARE @row_goal BIGINT = 9223372036854775807;

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON
        1=1
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1000000));

추정치는 1000000 행임을 알 수 있습니다.

1M 행

이들은 쿼리 최적화에 필요하지 않은 고급 기술이라는 점에주의해야합니다. 자세한 내용을 보려면 Adam Machanic이 제시 한 Clash of the Row Goals를 시청하는 것이 좋습니다 .


dbo. 많은 기능

-- By Adam Machanic, reproduced with permission
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Many' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo')
    DROP FUNCTION dbo.Many
GO
CREATE FUNCTION dbo.Many(@n INT)
RETURNS TABLE AS
RETURN
(
    WITH
    a(x) AS
    (
        SELECT
            *
        FROM
        (
            VALUES
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
        ) AS x0(x)
    )
    SELECT TOP(@n)
        1 AS x
    FROM
        a AS a1,
        a AS a2
    WHERE
        a1.x % 2 = 0
)
GO

9

카디널리티 추정을 옵티 마이저에 직접 주입하는 방법은 없지만 달성하려는 목표에 따라 몇 가지 옵션이 있습니다.

OPTION (FAST N)쿼리 힌트를 사용하여 행 목표를 소개하고 CTE 또는 하위 쿼리를 사용하여 쿼리를 다시 작성 TOP...ORDER BY하여 실행 계획의 다른 부분에 기반 행 목표 를 주입 할 수는 있지만 시작할 때 결과 쿼리가 얼마나 효율적인지 잘 모르겠습니다. 더 복잡한 구조로 놀아요.

자세한 설명 은 최적화 프로그램 내부 : 행 목표 심도 를 참조하십시오 .

옵티마이 저가 선택하는 연산자에 영향을 주려면 카디널리티 추정을 주입하려고 할 필요는 없지만 물리적 조인 연산자를 강요하기 위해 OPTION (MERGE JOIN)또는 이와 같은 것을 사용할 수 있습니다 OPTION (HASH JOIN).

이 기사에서는 힌트를 사용하여 계획에 영향을 미치는 방법에 대해 자세히 설명합니다. 힌트를 사용하여 실행 계획 제어

계획을 수정하려는 경우 계획 지침을 사용할 수도 있습니다.

실제 사용 사례가 무엇인지 명확하지 않으며 마일리지는 이러한 기술에 따라 다를 수 있습니다. 대부분의 경우 옵티마이 저가 결정하도록하고 옵티마이 저가 정보에 근거한 결정을 내릴 수 있도록 최신 통계를 확보하는 것이 좋습니다.


관련 Microsoft Connect 제안 : xor88의 쿼리에서 필터 선택 힌트를 지정할 수 있습니다. Microsoft는 다음과 같이 응답했습니다.

피드백을 주셔서 감사합니다. 이것의 잠재적 이점을 볼 수 있습니다. 일반적으로 우리는 자동 행동을 최대한 좋게 만들고 이러한 종류의 힌트가 필요하지 않도록 노력하지만 물론 다른 힌트도 많이 있습니다. 향후 릴리스에서는이를 고려하지만 Denali (11.0) 버전을 넘어 설 것입니다.

감사합니다.
Eric Hanson
프로그램 관리자
SQL Server 쿼리 처리


3

SQL Server OPTIMIZE FOR쿼리 힌트를 사용하여 컴파일하는 동안 실제 값 (매개 변수) 또는 알 수없는 값 (변수) 대신 힌트 값을 기반으로 카디널리티 추정을 강제 할 수 있습니다 . 자세한 내용은 SQL Server 설명서 의 쿼리 힌트 항목 을 참조하십시오.

예를 들어 아래 쿼리는 로컬 변수와 달리 전체 평균 카디널리티 대신 힌트 값에서 통계 히스토그램을 기반으로 행 수를 추정합니다.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
SELECT *
FROM dbo.Example
WHERE
    DateColumn BETWEEN  @StartDate AND @EndDate
OPTION(OPTIMIZE FOR(@StartDate = '20100101', @EndDate='20100101'));

유사하게, 힌트는 파라미터가 추정 동안 컴파일 동안 실제 파라미터 값 대신 힌트 된 값으로부터의 통계 히스토그램에 기초하도록 파라미터에 사용될 수있다.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
EXECUTE sp_executesql N'SELECT *
        FROM dbo.Example
        WHERE
            DateColumn BETWEEN  @StartDate AND @EndDate
        OPTION(OPTIMIZE FOR(@StartDate = ''20100101'', @EndDate=''20100101''));'
    , N'@StartDate datetime, @EndDate datetime'
    , @StartDate = @StartDate
    , @EndDate = @EndDate;

UNKNOWN키워드 대신 실제 파라미터 값과 히스토그램 통계에 기초하여 추정하는 전체 평균 카디널리티를 사용하는 대신 힌트 리터의 특정 될 수있다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.