태그 보관물: cursors

cursors

sp_cursorprepexec는 5,500 만 건의 읽기를 유발합니까? bigint,@P22 bigint,@P23 bigint,@P24 bigint’,N’SELECT T1.PRODUCT,T1.EXTERNALVENDPARTY,T1.LIFECYCLESTATUS,T1.RECID,T2.ECORESPRODUCT,T2.ECORESDISTINCTPRODUCTVARIANT,T2.SGE,T2.ECORESREFORDERNUM,T2.ORDERNUM,T2.RECID,T3.ECORESREFORDERNUM,T3.NAME1,T3.NAME2,T3.NAME3,T3.RECID,T4.ECORESPRODUCT,T4.EXTERNALITEMID,T4.ECORESDISTINCTPRODUCTVARIANT,T4.RECID,T5.RECID,T5.PERSON,T6.RECID,T6.NAME,T6.INSTANCERELATIONTYPE,T7.RECID,T7.NAME,T7.INSTANCERELATIONTYPE,T8.PARTY,T8.ACCOUNTNUM,T8.RECID,T9.RECID,T9.DISPLAYPRODUCTNUMBER,T9.INSTANCERELATIONTYPE,T10.PRODUCT,T10.CATEGORY,T10.RECID,T11.RECID,T11.CODE,T11.NAME,T11.INSTANCERELATIONTYPE

SQL Server 2012와 함께 Dynamics AX 2012 설치를 실행하고 있습니다. 더 이상 커서를 사용해서는 안되지만 AX가 커서를 사용하고 있으므로이 동작을 변경할 수 없으므로 작업해야합니다.

오늘 나는 5,500 만 건 이상의 읽기와 20 분 이상의 실행 시간으로 매우 나쁜 쿼리를 발견했습니다.

모니터링 도구 SentryOne을 통해이 쿼리를 발견했습니다.

declare @p1 int
set @p1=1073773227
declare @p2 int
set @p2=180158805
declare @p5 int
set @p5=16
declare @p6 int
set @p6=1
declare @p7 int
set @p7=2
exec sp_cursorprepexec @p1 output,@p2 output,N'@P1 bigint,@P2 nvarchar(5),@P3 bigint,@P4 nvarchar(8),@P5 bigint,@P6 bigint,@P7 bigint,@P8 bigint,@P9 bigint,@P10 bigint,@P11 bigint,@P12 bigint,@P13 bigint,@P14 bigint,@P15 bigint,@P16 bigint,@P17 bigint,@P18 bigint,@P19 nvarchar(5),@P20 bigint,@P21 bigint,@P22 bigint,@P23 bigint,@P24 bigint',N'SELECT T1.PRODUCT,T1.EXTERNALVENDPARTY,T1.LIFECYCLESTATUS,T1.RECID,T2.ECORESPRODUCT,T2.ECORESDISTINCTPRODUCTVARIANT,T2.SGE,T2.ECORESREFORDERNUM,T2.ORDERNUM,T2.RECID,T3.ECORESREFORDERNUM,T3.NAME1,T3.NAME2,T3.NAME3,T3.RECID,T4.ECORESPRODUCT,T4.EXTERNALITEMID,T4.ECORESDISTINCTPRODUCTVARIANT,T4.RECID,T5.RECID,T5.PERSON,T6.RECID,T6.NAME,T6.INSTANCERELATIONTYPE,T7.RECID,T7.NAME,T7.INSTANCERELATIONTYPE,T8.PARTY,T8.ACCOUNTNUM,T8.RECID,T9.RECID,T9.DISPLAYPRODUCTNUMBER,T9.INSTANCERELATIONTYPE,T10.PRODUCT,T10.CATEGORY,T10.RECID,T11.RECID,T11.CODE,T11.NAME,T11.INSTANCERELATIONTYPE FROM INVENTTABLE T1 CROSS JOIN ECORESPRODUCTORDERNUM T2 CROSS JOIN ECORESPRODUCTORDERNUMTRANSLATION T3 LEFT OUTER JOIN VENDEXTERNALITEM T4 ON ((T4.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T4.ECORESPRODUCT) AND (T4.ECORESDISTINCTPRODUCTVARIANT=@P1))) CROSS JOIN HCMWORKER T5 CROSS JOIN DIRPARTYTABLE T6 CROSS JOIN DIRPARTYTABLE T7 CROSS JOIN VENDTABLE T8 CROSS JOIN ECORESPRODUCT T9 CROSS JOIN ECORESPRODUCTCATEGORY T10 CROSS JOIN ECORESCATEGORY T11 WHERE (((T1.PARTITION=5637144576) AND (T1.DATAAREAID=N''087'')) AND (T1.DATAAREAID=@P2)) AND ((T2.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T1.PRODUCT) AND (T2.SGE=@P3))) AND ((T3.PARTITION=5637144576) AND ((T3.ECORESREFORDERNUM=T2.ECORESREFORDERNUM) AND (T3.LANGUAGEID=@P4))) AND ((T5.PARTITION=5637144576) AND (T5.RECID=T2.PRODUCTMANAGER)) AND (((T6.PARTITION=5637144576) AND (T6.INSTANCERELATIONTYPE IN (@P5,@P6,@P7,@P8,@P9,@P10,@P11) )) AND (T6.RECID=T5.PERSON)) AND (((T7.PARTITION=5637144576) AND (T7.INSTANCERELATIONTYPE IN (@P12,@P13,@P14,@P15,@P16,@P17,@P18) )) AND (T1.EXTERNALVENDPARTY=T7.RECID)) AND (((T8.PARTITION=5637144576) AND (T8.DATAAREAID=N''087'')) AND ((T7.RECID=T8.PARTY) AND (T8.DATAAREAID=@P19))) AND (((T9.PARTITION=5637144576) AND (T9.INSTANCERELATIONTYPE IN (@P20,@P21,@P22) )) AND (T9.RECID=T1.PRODUCT)) AND ((T10.PARTITION=5637144576) AND (T10.PRODUCT=T9.RECID)) AND (((T11.PARTITION=5637144576) AND (T11.INSTANCERELATIONTYPE IN (@P23,@P24) )) AND (T11.RECID=T10.CATEGORY))',@p5 output,@p6 output,@p7 output,0,N'087',5637146082,N'de',41,2303,2377,2975,2978,5329,6886,41,2303,2377,2975,2978,5329,6886,N'087',3265,3266,3267,2665,4423
select @p1, @p2, @p5, @p6, @p7

가장 먼저 눈에 띄는 것은이 쿼리가 커서를 사용하고 있다는 것입니다. 호기심으로 인해 명령문을 복사하고 커서가없는 Management Studio에서 실행했습니다 (쿼리의 매개 변수를 바꿔서 실행할 수 있음을 인정해야합니다). SSMS 내에서 쿼리는 30 초 후에 완료되었습니다. 그리 빠르지는 않지만 커서 대안보다 여전히 빠릅니다.

여기에 두 가지 계획을 모두 제공합니다.

커서가없는 계획은 여전히 ​​매우 나쁜 계획이지만 훨씬 좋습니다. 내 질문은 여기에 있습니다 : 누군가 커서 버전에 5,500 만 읽기가 필요한 이유를 설명해 주시겠습니까?

커서를 이용한 쿼리 통계 :

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
1.396.212   1.379.157   53.270.895  3.878   30          2

커서없는 쿼리에 대한 통계 :

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
23.337      1.703       665.113     13      4.287       34.813

2 대신 34,813 개의 행을 얻는 것이 이상하게 보입니다. 그러나 올바른 매개 변수를 입력했다고 확신합니다. 방금 통계를 복사 한 이후로 SQL Sentry의 재미있는 단점이라고 생각했습니다.

필요한 모든 정보를 제공 할 수 있기를 바랍니다. 또한 누군가가 좋은 읽을 거리를 가지고 있다면 커서가 잘 이해 될 것입니다.



답변

우선, SQL Sentry의 두 쿼리에 대한 실제 행 수가 거의 동일하지 않다는 것이 놀랍습니다.

둘째. 실제 계획이없는 커서를 사용하여 계획에서 추정치가 얼마나 정확한지 말하기는 어렵지만 어떤 것이 나에게 두드러집니다. (PS : 실제 계획을 얻으려면 여기내 대답을 참조하십시오 ).

즉, 예상 계획에서 확인할 수있는 몇 가지 사항이 있습니다.

매개 변수화로 인해 일치 하지 않는 인덱스 에 대한 경고가 있습니다 . SQL Server가 일치하지 않는 것을 사용할 수 있도록 매개 변수화를 제거하면 I / O가 크게 향상 될 수 있습니다.

두 계획 사이의 예상 행 수가 너무 적습니다. 커서가있는 계획의 경우 vendexternalitem에서 11의 예상 행 수가 있습니다. 커서가없는 계획의 경우 거의 200K의 예상 및 실제 행 수가 있습니다. 200K 레코드가 실제로 해당 스풀 연산자에 들어가면 고통 스러울 수 있습니다.

모든 연산자의 추정치가 크게 다르기 때문에 (커서가있는 계획에서 훨씬 더 작음) 계획이 커서없이 쿼리에서 사용하는 것과 다른 매개 변수 값으로 컴파일 및 캐시되었을 수 있습니다. ( 매개 변수 스니핑 이라고 함 )

발명 테이블의 인덱스 탐색 + 키 조회에서도 매우 이상한 선택이 있습니다. 계획은 typeIdx를 사용하고 클러스터 된 인덱스 (itemidx)에 대한 키 조회를 수행합니다. 추정치가 나와 있지 않고 SQL Server는 많은 IO를 설명 할 수있는 많은 주요 조회를 수행해야합니다. 커서없이 계획에있는 stopidx에 익숙하지 않지만 마치 덮고있는 것처럼 보이므로 제공 한 매개 변수에 더 적합한 선택 일 수 있습니다. 나는 그것이 훨씬 좁기 때문에 typeidx를 선택했다고 가정하지만 문제가있는 실행을 제공하는 것과는 다른 컴파일 시간 값 때문일 수 있습니다.

간단히 말해서, AX에서이 쿼리에 대한 매개 변수화를 제거하여 실제 값으로 계획을 생성하므로 SSMS에서 계획 (및 실행 시간)에 의해 입증 된 “더 나은”인덱스를 선택합니다. 또한 SQL Server에서 필터링 된 인덱스를 사용할 수 있습니다. 그렇게하려면 개발자 에게이 쿼리가 실행되는 응용 프로그램 코드에 forceliterals키워드 를 추가하고 어떻게되는지 확인하십시오.

아마도 SSSS에있는 것과 비슷한 30 초의 쿼리가 여전히 남아있을 것입니다. 그러나 그것은 단지 튜닝의 문제입니다. 계획에 누락 된 인덱스 경고가 있으며 ecoresproductordernum.sge의 인덱스가 도움이 될 수 있다고 생각하지만 해당 테이블을 모르고 사용자 정의로 추가 된 것으로 생각합니다. 일반적인 튜닝 원칙은 여기에 도움이 될 것이지만 아마도이 답변에 대해서는 너무 광범위 할 것입니다 (색인 포함 …)


답변