Wir führen eine Dynamics AX 2012-Installation mit SQL Server 2012 aus. Ich weiß, dass Cursor nicht mehr verwendet werden sollten, aber AX verwendet sie und wir können dieses Verhalten nicht ändern, sodass wir damit arbeiten müssen.
Heute habe ich eine sehr schlechte Anfrage mit über 53 Millionen Lesevorgängen und einer Ausführungszeit von mehr als 20 Minuten erhalten.
Ich habe diese Abfrage über unser Überwachungstool SentryOne abgefangen.
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
Das erste, was mir aufgefallen ist, ist, dass diese Abfrage einen Cursor verwendet. Aus Neugier habe ich die Anweisung kopiert und in Management Studio ohne Cursor ausgeführt (ich muss zugeben, dass ich die Parameter für die Abfrage ersetzt habe, damit ich sie ausführen kann). Innerhalb von SSMS wurde die Abfrage in 30 Sekunden beendet. Nicht sehr schnell, aber immer noch schneller als die Cursor-Alternative.
Hier biete ich Ihnen beide Pläne:
- Planen Sie mit dem Cursor: https://www.brentozar.com/pastetheplan/?id=Sk0aMY-Y-
- Planen Sie ohne Cursor: https://www.brentozar.com/pastetheplan/?id=HJ6ImtWK-
Der Plan ohne Cursor ist immer noch ein sehr schlechter Plan, aber er ist viel besser. Meine Frage hier ist: Kann mir bitte jemand erklären, warum die Cursorversion 53 Millionen Lesevorgänge benötigt?
Statistiken für die Abfrage mit dem Cursor:
Duration CPU Reads Writes Est Rows Actual Rows
1.396.212 1.379.157 53.270.895 3.878 30 2
Statistiken für die Abfrage ohne Cursor:
Duration CPU Reads Writes Est Rows Actual Rows
23.337 1.703 665.113 13 4.287 34.813
Es scheint seltsam, 34.813 Zeilen anstelle von 2 zu erhalten. aber ich bin mir ziemlich sicher, dass ich die richtigen Parameter eingegeben habe. Ich dachte, dass dies vielleicht eine lustige Eigenart von SQL Sentry ist, da ich gerade die Statistiken von dort kopiert habe.
Ich hoffe, ich konnte Ihnen alle notwendigen Informationen für Sie zur Verfügung stellen. Auch wenn jemand gute Lesungen hat, kann er Cursor besser verstehen, das wäre großartig.