2012-05-03 2 views
1

오늘 회의가 너무 많았지 만 두뇌가 여전히 남아 있다고 생각합니다. 나는 다음과 같은 신비 건너 온 일부 쿼리의 성능을 개선하기위한 내 노력의 일환으로 (테이블 이름과 필드 의역) : 당신이 볼 수 있듯이SQL Server 쿼리 성능 미스터리

SELECT X.ADId FROM 
(
    SELECT DISTINCT A.ADId 
    FROM P WITH (NOLOCK) 
    INNER JOIN A WITH (NOLOCK) ON (P.ID = A.PId) 
    INNER JOIN dbo.fn_A(16) AS VD ON (VD.DId = A.ADId) 
    LEFT JOIN DPR ON (LDID = A.ADId) 
    WHERE ((A.ADId = 1) OR ((HDId IS NOT NULL) AND (HDId = 1))) AND 
      (P.PS NOT IN(5,7)) AND (A.ASP IN (2, 3)) 
) X 
WHERE (dbo.fn_B(X.ADId, 16) = 1) 

는 내부 쿼리의 내용은 대부분 관련이 있습니다. 처음에는 모든 레코드에 대해 ADId에 중복 값이 ​​포함 된 fn_B()가 발생하지 않도록하고 싶었 기 때문에 SELECT DISTINCT를 사용하여 내부적으로 개별 레코드를 필터링했습니다. 소리가 합리적입니까? 여기

내부 쿼리 (지정된 매개 변수에 대한) 레코드를 반환 ... 신비를 시작합니다. "WHERE fn_B() = 1"을 주석 처리하면 쿼리가 0 시간에 실행되고 결과가 반환되지 않습니다. 다시 입력하면 쿼리에 6-10 초가 걸리고 결과가 반환되지 않습니다.

이것은 내 상식을 뛰어 넘는 것처럼 보입니다. 내 공통 SQL 의미 :-) 내부 쿼리가 데이터를 반환하지 않으면 외부 조건이 올바르게 평가되지 않아야합니까?

물론 실제 실행 계획을 확인하고 저장 한 다음 매우 신중하게 비교했습니다. 그것들은 99 % 동일하며, 특별한 통지가 없기 때문에 생각합니다.

일부 CTE를 속이고 첫 번째 CTE에서 쿼리 결과를 얻은 다음 레코드를 필터링하지 않도록 보장 된 몇 가지 조건이있는 두 번째 CTE로 전달한 다음 모든 CTE 외부에서 fn_B() 호출을 평가하지만 그 행동은 정확히 동일했다.

이전 쿼리 (같은 값으로 fn_B()를 여러 번 호출 할 수 있음)를 사용하는 것과 같은 다른 변형도 동일한 동작을합니다. 조건을 제거하면 제로 시간에 기록이 없습니다. 다시 넣으면 10 초 안에 아무런 기록도 남기지 않습니다.

아무도 아이디어가 없습니까?

PS1 :-) 시간에 대한

감사합니다 : 나는 간단한 쿼리를 사용하여 tempdb에의 ​​상황을 재현하려하지만 난 그렇게 할 수 없었다. 그것은 내 실제 테이블에서만 발생합니다. PS2 :이 쿼리는 다른 함수 내에서 호출되므로 결과를 임시 테이블에 저장 한 다음 추가로 필터링하는 것도 문제가되지 않습니다.

답변

0

메모와 마찬가지로 옵티마이 저는 사용자와 동일한 방식으로 쿼리를 읽지 않습니다. 특정 순서가 발생해야한다고 생각하거나 단락이 가장 적절하다고 생각할 때도 옵티마이 저는 예상하지 못한 순서로 CTE/하위 쿼리를 계속 평가할 수 있습니다. 일시적인 해결책은 #temp 테이블에 첫 번째 쿼리를 선택한 다음 #temp 테이블에서 함수 필터를 실행하는 것입니다. 완전히 직관력이없고 훨씬 덜 우아하더라도 평가 순서를 강제해야합니다.

편집 또한

, 그것은 느리게 수행 할 수있는 동안, 당신이 대신 NOLOCK없이 쿼리를 실행하거나 RCSI의 경우 발생하는 궁금합니다. 잠금 의미가 다르면 옵티 마이저가 작동하지 않을 수 있습니다.

+0

대부분의 사람들은 옵티마이 저가 이상한 방식으로 내용을 재 배열한다는 것을 이미 알고 있다고 생각합니다. 그러나 때때로 쿼리는 프로그래머가 무슨 일이 일어나는지를 담당하는 방식으로 작성됩니다. 내가 두 개의 SELECT DISCTINCT를 실행 한 다음에 조인하면, 나는 대략 무슨 일이 일어날 지 확신합니다. 어쨌든, 오늘 내부 질의가 실제로 데이터를 가져 오는 호출이나 fn_B()를 더미 함수로 대체하는 호출을 시도해보고 동작이 어떻게 바뀌는 지 살펴 보겠습니다. –

+0

그렇게 생각 하겠지만 예외는 많습니다. 최적화 프로그램이 완벽하지 않습니다. 오늘 Hugo의 블로그 게시물과 버그 보고서를 읽었습니까? http://sqlblog.com/blogs/hugo_kornelis/archive/2012/05/04/the-curious-case-of-the-optimizer-that-doesn-t.aspx 필자는 몇 가지 사례를 보았다. 옵티마이 저가 쿼리를 개별 쿼리로 나눌 것으로 예상되는 동작을 적용합니다. 위에서 제안한대로 시도 할 수있는 아이디어 일뿐입니다. –

+0

추가 정보 나는 6 행을 반환 params와 내부 쿼리를 실행합니다. 제로. WHERE ==> 30 초를 추가하십시오. 나는이 ID들로 총 0 초간 fn (B)에 6 개의 명시 적 호출을했다. SQL Server는 SAME 5 테이블에서 Table Scans를 반복적으로 시작하여 (프로파일 러 로그에 약 100.000 개의 항목이 있음) SQL Server에서 다음을 수행합니다. 질문. 이 모든 테이블은 fn_B() 내부에 나타나며 원래 예제에서는 호출되지 않습니다. NOLOCK을 제거해도 아무런 차이가 없습니다. 그래서 뭔가 SQL 서버가 혼란 스럽다는 것을 알기 시작했습니다. –

0

SQL Server R2에 대한 Microsoft의 지원에 문제가 제출되었습니다 (놀라운 응답 시간과 전체 서비스 절차에 대해 언급해야합니다). 우리는 그들에게 문제를 재현 우리 DB의 사본을주고, 우리의 해결 방법은, 그들은 스스로를 재현하고 여기에 며칠은 우리가 돌아 왔을 대답 후 :

내가 친절 것 모두 실행 계획을 분석 한 프로덕션 환경에서 해결 방법을 사용할 수 있는지 묻습니다. 주된 이유는 뒤에있는 이며, 인덱스에는있는 것처럼 함수에는 통계가 없습니다. 그리고 이러한 데이터 부족으로 옵티마이 저가 때로는 을 선택하게되므로 실행 계획이 좋지 않습니다. 해결 방법을 이미 찾은 경우이 방법을 구현하는 것이 가장 좋은 방법은 입니다. 시도한 색인 변경으로 인해 실행이 향상되지 않았습니다.

이것은 "외교관의 말로"예, 옵티마이 저는 쿼리를 엉망으로 만듭니다. 따라서 해결 방법을 사용하십시오. " 버그라고 부르려면 버그라고 부르면 문제가되지 않습니다.

단지 레코드의 경우 해결 방법은 fn_B()에 대한 호출을 SELECT DISTINCT보다 한 수준 위의 쿼리 SELECT 목록에 넣은 다음 WHERE 조건에 대한 결과를 필터링하는 것입니다. 별난 일이지만 트릭을합니다.