Um de nossos desenvolvedores está tentando executar a consulta abaixo em um servidor de desenvolvimento, que envolve extrair dados de um servidor vinculado, produção. A consulta foi executada por mais de 14 horas antes de ser interrompida.
Observei o plano de execução no SQL Sentry Plan Explorer - encontre o plano de execução abaixo.
Como essa consulta pode ser ajustada para melhor desempenho? Há algum erro flagrante na consulta? Existem dicas, postagens de blogs que me ajudarão a melhorar essa consulta?
Ambos os servidores envolvidos são SQL Server 2005
SELECT A.SETID
,A.CUST_ID
,A.CNTCT_SEQ_NUM
,A.NAME1
,A.TITLE
,C.DESCR
FROM PS_CUST_CONTACT A
,[linksrv].[prodDB].dbo.PS_BO_ROLE Z
,[linksrv].[prodDB].dbo.PS_RD_PERSON B
,[linksrv].[prodDB].dbo.PS_BO_ROLE_TYPE C
WHERE Z.BO_ID = B.BO_ID
AND Z.ROLE_TYPE_ID = C.ROLE_TYPE_ID
AND Z.ROLE_END_DT >= GETDATE()
AND A.EFFDT = (
SELECT MAX(EFFDT)
FROM PS_CUST_CONTACT CUST_CONTACT
WHERE CUST_CONTACT.SETID = A.SETID
AND CUST_CONTACT.CUST_ID = A.CUST_ID
AND CUST_CONTACT.CNTCT_SEQ_NUM = A.CNTCT_SEQ_NUM
AND CUST_CONTACT.EFFDT <= { FN CURDATE() }
)
AND A.EFF_STATUS = 'A'
AND B.PERSON_ID IN (
SELECT A1.PERSON_ID
FROM PS_CONTACT A1
,PS_CONTACT_CUST B1
WHERE A1.EFFDT = (
SELECT MAX(A_ED.EFFDT)
FROM PS_CONTACT A_ED
WHERE A1.SETID = A_ED.SETID
AND A1.CONTACT_ID = A_ED.CONTACT_ID
AND A_ED.EFFDT <= SUBSTRING(CONVERT(CHAR, GETDATE(), 121), 1, 10)
)
AND A1.SETID = B1.SETID
AND A1.CONTACT_ID = B1.CONTACT_ID
AND B1.EFFDT = (
SELECT MAX(B_ED.EFFDT)
FROM PS_CONTACT_CUST B_ED
WHERE B1.SETID = B_ED.SETID
AND B1.CONTACT_ID = B_ED.CONTACT_ID
AND B_ED.EFFDT <= A.EFFDT
)
AND A.CNTCT_SEQ_NUM = B1.CNTCT_SEQ_NUM
AND A.SETID = B1.CUSTOMER_SETID
AND A.CUST_ID = B1.CUST_ID
)
Para aprofundar o que Aaron afirma um pouco mais, o desempenho do servidor vinculado, especialmente para grandes conjuntos de resultados, para junções entre servidores e subconsultas entre servidores, geralmente é decepcionante. Se você observar o servidor remoto com o Profiler, poderá descobrir que o servidor local bombardeia o servidor remoto com solicitações de busca de uma única linha para corresponder às colunas de junção. Quando isso acontece, a latência da rede e a sobrecarga de chamadas conspiram para prejudicar o desempenho.
Se você puder consultar dados locais sem muitos problemas, seria melhor. Você pode restaurar um backup de produção ou usar SSIS (ou mesmo bcp, ainda funciona) para copiar dados do servidor de produção para algumas tabelas de trabalho no servidor local. Geralmente SSIS, bcp e táticas semelhantes são mais rápidas do que servidores vinculados e podem ajudar a evitar problemas com o crescimento do arquivo de log.
Se você precisar consultar os dados de um servidor remoto, poderá descobrir que reescrever a consulta para que ela use OPENQUERY() (em vez de nomes de quatro partes) e 'envie' todas as 'partes remotas' da consulta para o servidor remoto e, em seguida, junta os resultados disso aos dados locais será mais eficaz. O SQL deve ser inteligente o suficiente para mover todas as junções para o servidor remoto, mas às vezes isso não acontece e OPENQUERY () fornece um método para forçar o SQL a fazer o que você deseja.
Outra tática semelhante seria executar primeiro a 'parte remota' da consulta e colocar os resultados em uma tabela temporária, (opcionalmente) indexar a tabela temporária e depois juntar a 'parte local' da consulta à tabela temporária. Novamente, isso ajuda você a forçar o SQL a fazer o que deveria.
Parece mais trabalhoso, mas o SQL pode se comportar de maneira mais eficiente. Como sempre, observe seus tipos de dados nas junções e seus SARGs ou seus índices serão ignorados.