2013-01-14 4 views
1

SQL Server에서 쿼리를 작성하려고하는데 약 3 천만 개의 행 (TGS_INFO)이있는 테이블에서 테이블을 스캔하므로 쿼리가 매우 느리게 실행됩니다.SQL Server가 인덱스를 사용하지 않습니다

실제 쿼리는 더 복잡하지만 동일한 문제를 해결하는 간단한 버전으로 축소했습니다.

SELECT DISTINCT UNIT_ITEMS.DBKEY, 
    UNIT_ITEMS.ID, 
    UNIT_ITEMS.LOCATION1, 
    UNIT_ITEMS.LOCATION2 
FROM UNIT_ITEMS 
INNER JOIN TGS.dbo.TGS_INFO 
ON UNIT_ITEMS.UNIT_ID = TGS_INFO.UNIT_ID AND 
    UNIT_ITEMS.ITEM_ID = TGS_INFO.ITEM_ID AND 
    UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND 
    UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2 

다음은 실행 계획입니다.

StmtText 
    |--Sort(DISTINCT ORDER BY:([DbName].[dbo].[UNIT_ITEMS].[DBKEY] ASC, [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID] ASC, [DbName].[dbo].[UNIT_ITEMS].[LOCATION1] ASC, [DbName].[dbo].[UNIT_ITEMS].[LOCATION2] ASC)) 
     |--Hash Match(Inner Join, HASH:([DbName].[dbo].[UNIT_ITEMS].[UNIT_ID], [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID], [DbName].[dbo].[UNIT_ITEMS].[LOCATION1], [DbName].[dbo].[UNIT_ITEMS].[LOCATION2])=([Expr1008], [Expr1009], [Expr1010], [Expr1011]), RESIDUAL:([DbName].[dbo].[UNIT_ITEMS].[UNIT_ID]=[Expr1008] AND [DbName].[dbo].[UNIT_ITEMS].[ITEM_ID]=[Expr1009] AND [DbName].[dbo].[UNIT_ITEMS].[LOCATION1]=[Expr1010] AND [DbName].[dbo].[UNIT_ITEMS].[LOCATION2]=[Expr1011])) 
      |--Table Scan(OBJECT:([DbName].[dbo].[UNIT_ITEMS])) 
      |--Compute Scalar(DEFINE:([Expr1008]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[UNIT_ID],0), [Expr1009]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[ITEM_ID],0), [Expr1010]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[LOCATION1],0), [Expr1011]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[LOCATION2],0))) 
       |--Table Scan(OBJECT:([TGS].[dbo].[TGS_INFO])) 

TGS_INFO와 UNIT_ITEMS는 모두 UNIT_ID 및 ITEM_ID에 비 클러스터형 인덱스를 가지고 있습니다. 앞서 언급했듯이 TGS_INFO는 약 3 천만 개의 행을 가지고 있지만 약 1,000 개의 UNIT_ID에 대해 균등하게 분산되어 있습니다. UNIT_ITEMS에는 항상 UNIT_ID가 하나만 포함되어 있습니다.

여기 인덱스입니다

CREATE NONCLUSTERED INDEX [IX_UNIT_ID_ITEM_ID] ON [dbo].[TGS_INFO] 
(
    [UNIT_ID] ASC, 
    [ITEM_ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

CREATE NONCLUSTERED INDEX [IX_UNIT_ID_ITEM_ID] ON [dbo].[UNIT_ITEMS] 
(
    [UNIT_ID] ASC, 
    [ITEM_ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

내가 코멘트에서 언급 한 바와 같이, 모든 열은 TGS_INFO에서 VARCHAR (50)입니다. UNIT_ITEMS의 모든 열은 int입니다.

레코드의 경우 TGS_INFO 스키마를 디자인하지 않았습니다.

+1

테이블 당 하나의 클러스터 된 인덱스 만 가질 수 있습니다. 색인의 열 순서는 무엇입니까? – Kermit

+2

맞아, 해시가 틀린 것 같습니다. 또한 DISTINCT없이 시도 했습니까? DISTINCT가 정말로 필요한가요? –

+1

일치하는 모든 열에서 데이터 유형이 일치합니까? 암묵적인 데이터 변환을 수행하는 이유를 알 수 없습니다. 'UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2'을'WHERE' 절로 옮기면 어떨까요? –

답변

0

보여줍니다 다음

나는이 칼럼에 암시 적 데이터 형식 변환을 수행하는 쿼리 엔진에 대한 좋은 이유 생각할 수 없다
|--Compute Scalar(DEFINE:([Expr1008]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[UNIT_ID],0), [Expr1009]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[ITEM_ID],0), [Expr1010]=CONVERT_IMPLICIT(nvarchar(50),[TGS].[dbo].[TGS_INFO].[LOCATION1],0), [Expr1011]=CONVERT_IMPLICIT(int,[TGS].[dbo].[TGS_INFO].[LOCATION2],0))) 

둘 사이의 데이터 유형하지 않는 한 테이블이 조인에 사용중인 열과 일치하지 않습니다.

UNIT_ITEMS.LOCATION1 = TGS_INFO.LOCATION1 AND UNIT_ITEMS.LOCATION2 = TGS_INFO.LOCATION2을 인덱스로 덮어 쓰지 않으므로 WHERE 절로 옮길 수도 있습니다. 쿼리 엔진은 일반적으로 이것을 설명 할만큼 똑똑하지만 시도해 볼만한 것입니다.

+0

where 절로 물건을 옮기는 것은 SQL Server에서 작동하는 것이 아닙니다. 이 트릭은 주로 MySQL과 같은 약한 쿼리 최적화 프로그램이있는 RDBMS에서 유용합니다. – usr

3

LOCATION1LOCATION2을 색인에 포함시키지 않으면 색인만으로는 조인을 만족시킬 수 없습니다. 이러한 열을 두 테이블의 인덱스에 추가하십시오.

쿼리에 참조 된 다른 모든 열을 포함해야 할 수도 있습니다. 내가 실행 계획을 알

+0

UNIT_ID, ITEM_ID, LOCATION1 및 LOCATION2은 TGS_INFO 테이블의 후보 키이므로 인덱스를 만들면 불필요하게 보입니다. . – Tmdean

+0

하지만 너는 틀릴거야. 모든 FK는 색인을 생성해야합니다. 모든 잠재 키는 PK로 사용되지 않더라도 고유 한 색인을 그룹으로 가져야합니다. 공식 PK만이 자동으로 색인을 얻습니다. 하나의 복합 색인에서이를 수행 할 수 있습니다. – HLGEM

+2

@Tmdean 당신은 성능이나 불필요한 것에 관심이 있습니까? –

관련 문제