외부 매개 변수화 된 'where'절이있는 뷰에서 호출 될 때 창 함수로 인해 실행 계획이 끔찍하다


10

오래 전에이 문제가 있었는데 나에게 맞는 해결 방법을 발견하고 잊어 버렸습니다.

그러나 이제 그에 대한 질문 이 있으므로이 문제를 기꺼이 제기하겠습니다.

몇 가지 테이블을 매우 간단한 방식으로 정렬하는 뷰가 있습니다 (오더 + 오더 라인).

where절 없이 쿼리 하면 뷰는 수백만 줄을 반환합니다.
그러나 아무도 그렇게 그렇게 부르지 않습니다. 일반적인 검색어는

select * from that_nasty_view where order_number = 123456;

이렇게하면 5m 중 약 10 개의 레코드가 반환됩니다.

중요한 것은 : 뷰에는 윈도우 함수가 포함되어 rank()있으며, 뷰는 항상 쿼리가 사용되는 필드로 정확하게 분할됩니다.

rank() over (partition by order_number order by detail_line_number)

이제이 뷰가 쿼리 문자열에서 리터럴 매개 변수로 쿼리되면 위와 같이 정확하게 행을 리턴합니다. 실행 계획은 괜찮습니다.

  • 인덱스는 인덱스를 사용하여 두 테이블 모두에서 검색 order_number합니다 (10 개의 행을 반환 함).
  • 작은 결과를 반환하는 창을 계산합니다.
  • 선택 중입니다.

그러나 뷰가 매개 변수화 된 방식으로 호출되면 상황이 악화됩니다.

  • Index scan인덱스를 무시하고 모든 테이블에. 5m 행을 반환합니다.
  • 큰 조인.
  • 모든 partitions에 대한 창 계산 (약 500k 창).
  • Filter 5m에서 10 개의 행을 가져옵니다.
  • 고르다

이는 매개 변수가 관련된 모든 경우에 발생합니다. SSMS 일 수 있습니다.

declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;

Excel과 같은 ODBC 클라이언트 일 수 있습니다.

select * from that_nasty_view where order_number = ?

또는 SQL 연결이 아닌 매개 변수를 사용하는 다른 클라이언트 일 수 있습니다.

창 함수가보기에서 제거되면 매개 변수로 조회되는지 여부에 관계없이 완벽하게 빠르게 실행됩니다.

내 해결 방법은 문제가되는 기능을 제거하고 나중에 다시 적용하는 것이 었습니다.

그러나 무엇을 제공합니까? SQL Server 2008에서 창 기능을 처리하는 방식이 실제로 버그입니까?


order_number는 기본 키입니까? 열의 데이터 유형과 매개 변수가 일치합니까?
gbn

order_number기본 키가 아닙니다. 그것은이다 int not null두 테이블에에 클러스터되지 않은 인덱스.
GSerg

5
SQL Server 2005에는 이 영역에서 술어 푸시 와 관련된 문제가있었습니다 . 나는 그들이 지금 고쳐 졌다고 생각했다. BTW TSQL 예제는 매개 변수가 아닌 변수를 사용합니다. 추가 않는 OPTION (RECOMPILE)도움을?
Martin Smith

1
@ GSerg-필터가 잘못된 계획에서 필터에 약 5 백만 개의 행이 있고 실제와 일치하는 것으로 추정되는 10 개의 행이 있습니까? 그렇다면 술어 푸시 문제가 여전히 완전히 해결되지 않은 것일 수 있습니다.
Martin Smith

답변:


5

이것은 오래 지속되는 문제로, 한 형태 또는 다른 형태로 재 포장을 계속하고 여전히 SQL Server 2012에 있습니다.

그것을 논의하는 일부 게시물은

2012 년까지의 모든 현재 SQL Server 버전 option(recompile)은 사용 된 경우 (2008+ 인 경우 )를 제외하고 매개 변수화 된 술어에 대해 시퀀스 프로젝트를지나 파티션 그룹의 필터를 푸시 할 수 없습니다 .

recompile힌트에 대한 대안 은 @ a1ex07에서 제안한대로 매개 변수화 된 인라인 TVF를 사용하도록 쿼리를 다시 작성하는 것입니다.


SQL Server 2014에서도 마찬가지
Guillaume86

3

뷰를 테이블 반환 udf로 바꾸려고합니다. 그렇게하면 먼저 레코드를 필터링 한 다음 창 기능을 적용합니다. 여러 전달할 수 있도록이 기능은 테이블 매개 변수를 수용 할 수 order_number그것으로


또 다른 해결 방법입니다. 모든 클라이언트가 테이블 반환 함수를 사용할 수있는 것은 아니기 때문에 그렇게 할 수 없었습니다.
GSerg

왜? 100 % 확신 할 수는 없지만 쿼리를 약간 변경하면됩니다SELECT * FROM my_funct(12345)
a1ex07

요구 사항 중 하나는 Excel을 사용하는 최종 사용자 (즉, MS Query)가 쿼리를 사용할 수 있어야한다는 것입니다. MS Query는 적어도 2003 년까지의 버전에서는이를 수행 할 수 없습니다.
GSerg

it will filter records first, and then apply window function부정확하다. 결정 론적 명령은 없다
Remus Rusanu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.