쿼리를 최적화하는 방법


9

이와 비슷한 데이터베이스 구조가 있습니다.

CREATE TABLE [dbo].[Dispatch](
    [DispatchId] [int] NOT NULL,
    [ContractId] [int] NOT NULL,
    [DispatchDescription] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Dispatch] PRIMARY KEY CLUSTERED 
(
    [DispatchId] ASC,
    [ContractId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[DispatchLink](
    [ContractLink1] [int] NOT NULL,
    [DispatchLink1] [int] NOT NULL,
    [ContractLink2] [int] NOT NULL,
    [DispatchLink2] [int] NOT NULL
) ON [PRIMARY]

GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (1, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (2, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (3, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (4, 1, N'Test')
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 2)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 3)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 3, 1, 2)
GO

DispatchLink 테이블의 요점은 두 개의 Dispatch 레코드를 서로 연결하는 것입니다. 그건 그렇고 레거시 때문에 디스패치 테이블에서 복합 기본 키를 사용하고 있기 때문에 많은 고통없이 키를 변경할 수 없습니다. 또한 연결 테이블이 올바른 방법이 아닐 수도 있습니다. 그러나 다시 레거시.

이 질문을 실행하면 내 질문

select * from Dispatch d
inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1
or d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2

DispatchLink 테이블에서 인덱스 검색을 수행 할 수는 없습니다. 항상 전체 인덱스 스캔을 수행합니다. 몇 개의 레코드로도 문제가 없지만 해당 테이블에 50000이 있으면 쿼리 계획에 따라 인덱스에서 50000 개의 레코드를 스캔합니다. 조인 절에 'ands'와 'ors'가 있기 때문에 SQL이 왜 'or'의 왼쪽에 대한 몇 가지 인덱스 검색을 수행 할 수 없는지에 대해 알 수 없습니다. '또는'의 오른쪽에 하나.

쿼리를 조정하지 않고 수행 할 수 없다면 쿼리를 더 빨리 만들라는 제안이 아니라 이에 대한 설명을 원합니다. 이유는 위의 쿼리를 병합 복제 조인 필터로 사용하고 있기 때문에 불행히도 다른 유형의 쿼리를 추가 할 수 없기 때문입니다.

업데이트 : 예를 들어 이들은 내가 추가 한 색인 유형입니다.

CREATE NONCLUSTERED INDEX IDX1 ON DispatchLink (ContractLink1, DispatchLink1)
CREATE NONCLUSTERED INDEX IDX2 ON DispatchLink (ContractLink2, DispatchLink2)
CREATE NONCLUSTERED INDEX IDX3 ON DispatchLink (ContractLink1, DispatchLink1, ContractLink2, DispatchLink2)

따라서 인덱스를 사용하지만 전체 인덱스에서 인덱스 스캔을 수행하므로 50000 레코드는 인덱스에서 50000 레코드를 스캔합니다.


DispatchLink테이블에 색인이 있습니까?
ypercubeᵀᴹ

위에서 시도한 색인을 추가했습니다.
피터

쿼리에서 : "select * from Dispatch d inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1 또는 d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2"제거 "OR"조건을 사용하고 각각 "OR"을 사용하지 않는 두 개의 SELECT 문 중 UNION으로 대체하십시오. 또한 가능한 한 순수하게 테스트하기 위해 "*"대신 두 SELECT의 유일한 키 열을 사용하십시오.
NoChance

감사합니다 SQL Kiwi, 이것은 이전에 시도했지만 불행히도 작동하지 않았습니다.
peter

1
보다 간단한 쿼리 복제 문제가있을 수 있습니다. d.DispatchId = dl.DispatchLink1 및 d.ContractId = dl.ContractLink1의 select * from Dispatch d inner join DispatchLink dl 예인 경우, 결과가 여전히 유효하도록 DispatchLink에서 데이터를 복제 할 수 있습니다. ...
AK

답변:


12

옵티마이 저는 여러 탐색 대안 (다중 탐색을 포함하는 계획 포함)을 고려할 수 있지만 분리 ( OR조건 자)의 경우 기본적으로 인덱스 교차를 포함하는 계획을 고려하지 않습니다. 주어진 색인 :

CREATE CLUSTERED INDEX cx 
ON dbo.DispatchLink (DispatchLink1, ContractLink1);

CREATE NONCLUSTERED INDEX nc1 
ON dbo.DispatchLink (DispatchLink2, ContractLink2);

인덱스 검색을 강제로 수행 할 수 있습니다 (SQL Server 2008 이상 가정).

SELECT * 
FROM dbo.Dispatch AS d
INNER JOIN dbo.DispatchLink AS dl WITH (FORCESEEK) ON 
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

FORCESEEK 계획

샘플 데이터를 사용하여,은 계획 비용 추구 0.0332551 에 비해 단위 0.0068057 검사 계획을 :

스캔 계획

우리가 시도 할 수있는 모든 종류의 가능한 쿼리 재 작성 및 힌트가 있습니다. 옵티마이 저가 원래 계획에 고려하지 않는 옵션을 승격시키기위한 재 작성의 한 예는 다음과 같습니다.

SELECT * 
FROM dbo.Dispatch AS d
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

이 실행 계획은 첫 번째 인덱스에서 일치하는 항목을 찾으면 두 번째 인덱스를 찾지 않습니다.

TOP 적용 플랜

기본 FORCESEEK계획 보다 성능이 약간 더 우수 할 수 있습니다 .

새로운 인덱스를 추가하지 않고 디스패치 테이블을 강제로 탐색 할 수도 있습니다.

SELECT * 
FROM dbo.DispatchLink AS dl
JOIN dbo.Dispatch AS d WITH (FORCESEEK) ON
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

탐색 2

이것은 각 테이블에 몇 개의 행이 있는지에 따라 첫 번째 예보다 좋거나 나쁠 수 있습니다. APPLY + TOP개선은 여전히 가능하다 :

SELECT * 
FROM dbo.DispatchLink AS dl
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

매우 유용한 답변입니다. 나는 실제 데이터 (내 테스트 데이터가 아닌)에 대한 실제 쿼리 계획을 보여주는 또 다른 질문 dba.stackexchange.com/questions/23773/analysing-a-query-plan 을 물었다 . 쿼리 계획의 병목 현상을 정확히 이해해야 할 지식이 없습니다. 아마도 당신은 볼 수 있습니까?
피터

'FORCESEEK'을 추가하면 쿼리가 10 분 이상 걸리지 않고 9 초 안에 실행되므로 정말 흥미 롭습니다. 통계 업데이트는 차이가 없습니다. 쿼리 분석기가 왜 그렇게 잘못했을까요?
피터

나는 당신이 디자인에 대해 옳다고 생각합니다. 반복되는 열에 대해 무엇을 의미합니까? 두 개의 디스패치 레코드를 서로 연관시켜야하는 테이블 구조를 어떻게 설계 하시겠습니까? '실제'테이블에 자체 기본 키 필드가 있지만 명확히하기 위해 Dispatch에 복합 키를 갖는 것이 정확히 도움이되지는 않습니다.
피터

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