2011-08-12 4 views
8

나는 천천히 매우 실행이 쿼리 (거의 분)이 있습니다이유는 하나 개의 쿼리가 매우 느린 유사한 테이블에 아직 동일한 쿼리가 눈 깜짝 할 사이에 실행됩니다

select distinct main.PrimeId 
from PRIME main 
join 
( 
select distinct p.PrimeId from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId or p.PrimeId = a.RelatedPrimeId  
where a.PrimeId is not null and a.RelatedPrimeId is not null 
) mem 
on main.PrimeId = mem.PrimeId 

국무 테이블을 18k 행을 가지며 PrimeId에 PK가 있습니다.

ATTRGROUP 테이블은 24k 개의 행을 가지며 PrimeId, col2, RelatedPrimeId 및 복합 4-7의 복합 PK를가집니다. RelatedPrimeId에는 별도의 색인도 있습니다. ATTRGROUP 대신 ATTRADDRESS를 사용하여, 나는 동일한 쿼리를 가지고있는 ATTRGROUP 테이블

에 PrimeId 또는 RelatedPrimeId 중 하나와 일치하는 PRIME 테이블에 PrimeId의 고유 값 -

쿼리는 결국 8.5k 행을 반환합니다. ATTRADDRESS는 ATTRGROUP과 동일한 키 W 색인 구조를가집니다. 그것은 단지 11k 행을 가지는데, 그보다는 더 작지만,이 경우 쿼리는 약 1 초 만에 실행되고 11k 행을 반환합니다.

그래서 제 질문은 이것이다 :

어떻게 쿼리가 동일한되는 구조에도 불구하고, 다른 것보다 하나 개의 테이블에 훨씬 느려질 수 있습니다.

지금까지 SQL 2005에서 (동일한 데이터베이스를 사용하여 업그레이드 한) SQL 2008 R2를 사용해 보았습니다. 우리 중 두 명은 동일한 결과를 두 개의 다른 컴퓨터에 독립적으로 복원하여 독립적으로 얻었습니다.

기타 사항 :

  • 대괄호 내의 비트는 이해가 안 실행 계획에 가능한 단서가에도 느린 쿼리
  • 에, 초 미만에서 실행됩니다. 여기의 부분은 의심스러운 320000000 행 조작으로,이다 :

enter image description here enter image description here

그러나, 그 테이블에 행의 실제 수는 320M, 24K 이상 조금 아닙니다! 그것이 OR보다는 UNION을 사용하도록

나는 따라서, 대괄호 내의 쿼리의 일부를 리팩토링하는 경우 :

select distinct main.PrimeId 
from PRIME main 
join 
( 
select distinct p.PrimeId from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId 
where a.PrimeId is not null and a.RelatedPrimeId is not null 
UNION 
select distinct p.PrimeId from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId  
where a.PrimeId is not null and a.RelatedPrimeId is not null 
) mem 
on main.PrimeId = mem.PrimeId 

은 ... 다음 느린 쿼리는 초 정도 소요됩니다.

나는 이것에 대한 통찰력을 크게 주셔서 감사합니다! 더 자세한 정보가 필요하면 알려 주시면 질문을 업데이트하겠습니다. 감사!

그런데이 예제에서 중복 가입이 있음을 알고 있습니다. 프로덕션에서는 모든 것이 동적으로 생성되고 괄호 안의 비트는 여러 가지 양식을 사용하기 때문에 쉽게 제거 할 수 없습니다.


편집 : 나는 ATTRGROUP에 인덱스를 재 구축 한

는 유의 한 차이가 없습니다.

편집 2 :

나는 따라서 임시 테이블을 사용하는 경우 :

select distinct p.PrimeId into #temp 
from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId or p.PrimeId = a.RelatedPrimeId  
where a.PrimeId is not null and a.RelatedPrimeId is not null 

select distinct main.PrimeId 
from Prime main join 
#temp mem 
on main.PrimeId = mem.PrimeId 

... 다시 원래 OUTER의 OR은 가입도 함께, 그것은 이하에서 실행을 초. 나는 임시 테이블을 싫어한다, 왜냐하면 항상 패배를 인정하는 것 같기 때문에 내가 사용할 리팩터링이 아니지만 그런 차이가 있다는 것이 흥미 롭다.

편집 3 : 통계를 업데이트

중 하나 차이가 없습니다.

지금까지 의견을 보내 주셔서 감사합니다.

+1

정말 좋은 질문입니다! –

+0

@ 루이즈 : 왜 그런 말을하니? –

+1

18K * 24K는 432M입니다. 320M의 야구장에서. 우연의 일치 일지 모르지만, 어쩌면 보일만한 곳일지도 모릅니다. –

답변

6

JOIN 절에서 OR 대신 두 개의 왼쪽 조인을 사용하는 것이 더 좋습니다. 그래서 대신 :

left outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId or p.PrimeId = a.RelatedPrimeId 

내가 제안 :

left outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId 
    left outer join ATTRGROUP a2 
    on p.PrimeId = a2.RelatedPrimeId  
+0

네, 저도 제 경험입니다. 여기에있는 리팩토링 담당자는 실제로 내 UNION보다 매우 빠르고 슬슬 빠르게 실행됩니다. 그러나 제 질문은 원래 쿼리를 리팩터링하는 방법에 관한 것이 아닙니다 (분명히해야하지만 !!). 그래서 한 테이블에서 느리게 실행되는 sooooooo의 이유입니다. 그러나 유사한 크기와 동일한 다른 테이블에서는 분명히 느려지지 않습니다. 색인 구조. – ChrisA

+0

+1. –

+0

@ChrisA에 가입하는 더 좋은 방법은 데이터의 구성도 마찬가지로 중요합니다. 예를 들어, 더 빠른 ATTRADDRESS 기반 쿼리에서 RelatedPrimeId가 항상 null 인 경우 SQL은 비효율적 인 OR 부분을 최적화합니다. –

1

이 직접 대답하지 않습니다,하지만 당신은 주에 FK의 ATTRGROUP.PrimeId에서 참조 제약과 ATTRGROUP.RelatedPrimeId을 가지고 있다면, 당신의 쿼리는이 훨씬 간단에 해당 하나 하나 개의 쿼리가 다른 것보다 하나 개의 테이블에 훨씬 느려질 수 없었던 이유를 이유는 해당 테이블에 대한 통계가 밖으로 있다는 것입니다

select PrimeId from ATTRGROUP a 
union 
select RelatedPrimeId from ATTRGROUP a 
0

하나 날짜가 잘못되어 잘못된 쿼리 계획을 선택하고 있습니다.

그러나 다른 사람들이 제안한 또는 절을 제거하는 리팩터링을 지원합니다. 당신은 것입니다 (물론 절 'null가 아닌'

select distinct main.PrimeId 
from PRIME main 
join 
( 
select distinct p.PrimeId from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId 
where *main.PrimeId = a.PrimeId* 
UNION 
select distinct p.PrimeId from PRIME p 
left outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId  
where *main.PrimeId = a.PrimeId* 
) mem 
on main.PrimeId = mem.PrimeId 

이 구성에 당신이를 사용할 필요가 없습니다 :

+0

제안 해 주셔서 감사합니다. 나는 통계를 갱신했지만, 다시 차이는 없다 / – ChrisA

3

내가 메인 쿼리는 서브 쿼리와 관련되지 않은 것을 알 수 기본 키는 절대로 null 값을 보유하지 않으므로 필요합니까?).

나는 (다른 사람들이 이미 알고있는 것처럼) OR 구조를 피하고 또한 'null이 아님'또는 'valuelist'- 건설을 피하도록 가르침을 받았다. 그것들은 대체로 (NOT) EXISTS 절로 대체 될 수 있습니다.