Estou tendo um problema de desempenho com uma consulta que não consigo entender.
Tirei a consulta de uma definição de cursor.
Esta consulta leva segundos para ser executada
SELECT A.JOBTYPE
FROM PRODROUTEJOB A
WHERE ((A.DATAAREAID=N'IW')
AND ((A.CALCTIMEHOURS<>0)
AND (A.JOBTYPE<>3)))
AND EXISTS (SELECT 'X'
FROM PRODROUTE B
WHERE ((B.DATAAREAID=N'IW')
AND (((((B.PRODID=A.PRODID)
AND ((B.PROPERTYID=N'PR1526157') OR (B.PRODID=N'PR1526157')))
AND (B.OPRNUM=A.OPRNUM))
AND (B.OPRPRIORITY=A.OPRPRIORITY))
AND (B.OPRID=N'GRIJZEN')))
AND NOT EXISTS (SELECT 'X'
FROM ADUSHOPFLOORROUTE C
WHERE ((C.DATAAREAID=N'IW')
AND ((((((C.WRKCTRID=A.WRKCTRID)
AND (C.PRODID=B.PRODID))
AND (C.OPRID=B.OPRID))
AND (C.JOBTYPE=A.JOBTYPE))
AND (C.FROMDATE>{TS '1900-01-01 00:00:00.000'}))
AND ((C.TODATE={TS '1900-01-01 00:00:00.000'}))))))
GROUP BY A.JOBTYPE
ORDER BY A.JOBTYPE
O plano de execução real se parece com isso.
Percebendo que a configuração de todo o servidor foi definida como MaxDOP 1, tentei brincar com as configurações de maxdop.
Adicionar OPTION (MAXDOP 0)
à consulta ou alterar as configurações do servidor resulta em um desempenho muito melhor e neste plano de consulta.
No entanto, o aplicativo em questão (Dynamics AX) não executa consultas como esta, ele usa cursores.
O código real capturado é este.
declare @p1 int
set @p1=189527589
declare @p3 int
set @p3=16
declare @p4 int
set @p4=1
declare @p5 int
set @p5=2
exec sp_cursoropen @p1 output,N'SELECT A.JOBTYPE FROM PRODROUTEJOB A WHERE ((A.DATAAREAID=N''IW'') AND ((A.CALCTIMEHOURS<>0) AND (A.JOBTYPE<>3))) AND EXISTS (SELECT ''X'' FROM PRODROUTE B WHERE ((B.DATAAREAID=N''IW'') AND (((((B.PRODID=A.PRODID) AND ((B.PROPERTYID=N''PR1526157'') OR (B.PRODID=N''PR1526157''))) AND (B.OPRNUM=A.OPRNUM)) AND (B.OPRPRIORITY=A.OPRPRIORITY)) AND (B.OPRID=N''GRIJZEN''))) AND NOT EXISTS (SELECT ''X'' FROM ADUSHOPFLOORROUTE C WHERE ((C.DATAAREAID=N''IW'') AND ((((((C.WRKCTRID=A.WRKCTRID) AND (C.PRODID=B.PRODID)) AND (C.OPRID=B.OPRID)) AND (C.JOBTYPE=A.JOBTYPE)) AND (C.FROMDATE>{TS ''1900-01-01 00:00:00.000''})) AND ((C.TODATE={TS ''1900-01-01 00:00:00.000''})))))) GROUP BY A.JOBTYPE ORDER BY A.JOBTYPE ',@p3 output,@p4 output,@p5 output
select @p1, @p3, @p4, @p5
resultando neste plano de execução (e, infelizmente, os mesmos tempos de execução de vários segundos).
Eu tentei várias coisas, como descartar planos em cache, adicionar opções na consulta dentro da definição do cursor, ... Mas nenhum deles parece me dar um plano paralelo.
Também pesquisei bastante no google procurando por limitações de paralelismo de cursores, mas não consigo encontrar nenhuma limitação.
Estou perdendo algo óbvio aqui?
A compilação real do SQL é SQL Server 2008 (SP1) - 10.0.2573.0 (X64)
que eu percebo não ser suportada, mas não posso atualizar esta instância como considero adequado. Eu precisaria transferir o banco de dados para outro servidor e isso significaria obter um backup descompactado bastante grande em uma WAN lenta.
O sinalizador de rastreamento 4199 não faz diferença, nem OPTION (RECOMPILE).
As propriedades do cursor são:
API | Fast_Forward | Read Only | Global (0)
FAST_FORWARD
os cursores não suportam paralelismo (embora o servidor que gera o plano precise ser 2012 ou superior para obterNonParallelPlanReason
como parte do showplan XML).Quando você especifica
FAST_FORWARD
, o otimizador escolhe entreSTATIC
eDYNAMIC
para você.O plano de execução fornecido mostra o otimizador escolhendo um plano estático. Como a consulta contém agregação, duvido que um plano de cursor dinâmico seja possível aqui. No entanto, solicitar um
FAST_FORWARD
tipo de cursor está impedindo um plano paralelo.Você deve alterar o tipo de cursor explicitamente para
STATIC
ouKEYSET
, por exemplo. Ambos os tipos de cursor podem usar paralelismo.Dito isso, como esse é um cursor de API, alterar o tipo de cursor provavelmente exigiria uma alteração no aplicativo. Naturalmente, você precisaria comparar o desempenho para verificar se alterar o tipo de cursor é realmente a melhor opção para você.