Estou enfrentando um problema com o desempenho da consulta do SQL Server e espero obter alguns insights de todos vocês.
Declaração do problema:
Eu tenho uma consulta SQL que funciona bem (~6 ms) quando executada diretamente no SQL Server Management Studio (SSMS), mostrando uma "Busca de índice" no plano de execução. No entanto, quando a mesma consulta é executada em meu aplicativo Java usando JDBC, o plano de execução mostra uma "Varredura de índice" e a consulta leva muito mais tempo (~ 1 segundo) para ser executada.
O que eu tentei:
- Garantiu que as estatísticas estejam atualizadas nas tabelas e índices envolvidos.
- Comparamos as configurações de contexto de execução entre o SSMS e o aplicativo Java.
- Adicionado SQL ao produto armazenado e então funciona bem, faz busca de índice.
- ID do plano removido, cache limpo.
Pergunta:
- O que poderia estar causando essa discrepância no desempenho da consulta?
- Há alguma etapa de depuração que eu possa ter perdido?
Planeje quando isso será executado em Java: https://www.brentozar.com/pastetheplan/?id=SkWWezsya
Planeje quando isso será executado a partir do SSMS: https://www.brentozar.com/pastetheplan/?id=HJZPefokp
Quaisquer ideias ou sugestões serão muito apreciadas.
Os planos de consulta em sua pergunta mostram que o tipo de dados do parâmetro SSMS é
varchar(8000)
enquanto o tipo de parâmetro JDBC énvarchar(4000)
. O plano SSMS é capaz de usar um operador de busca eficiente porque ovarchar
tipo de parâmetro corresponde aovarchar
tipo de coluna, mas a conversão implícita da coluna nonvarchar
plano JDBC por regras de precedência de tipo de dados resulta em uma expressão não sargável, exigindo uma varredura.Acrescentarei que a conversão implícita não seria um grande problema com um agrupamento do Windows porque o SQL Server poderia forçar uma expressão sargável com um predicado de busca de intervalo usando GetRangeThroughConvert .
Você pode transmitir
varchar
parâmetros de aplicativos JDBC especificando sendStringParametersAsUnicode=false na cadeia de conexão. Trecho relevante do documento JDBC:Se você também precisar passar parâmetros Unicode, use os
setN
métodos deSQLServerPreparedStatement
:A respeito de:
O parâmetro do procedimento armazenado foi aparentemente definido como
varchar
. Nesse caso, onvarchar
valor do parâmetro fornecido pelo aplicativo foi convertido implicitamente novarchar
parâmetro definido pelo proc antes da execução do procedimento. O plano de execução do proc utilizou então ovarchar
parâmetro sem nenhuma conversão, permitindo a busca do índice.Vale ressaltar que os procedimentos armazenados, com tipos de parâmetros adequados, podem ajudar a mitigar problemas com tipos de parâmetros inadequados passados por aplicativos clientes porque os procedimentos são compilados e otimizados com a definição de parâmetro proc em vez da definição de parâmetro fornecida pelo cliente.
Sim, conforme comentários de @DanGuzman e @DenisRubashkin, você tem um parâmetro nvarchar.
Você terá que coagir seu cliente a enviar como varchar.
Vejo isso o tempo todo em .NET/Entity Framework/LINQ. É insidioso porque funciona em pequenos conjuntos de dados de desenvolvimento/teste/UAT, mas problemas de desempenho se manifestam em cargas de trabalho e conjuntos de dados de produção.
Parâmetros adicionados com AddWithValue ou similar farão isso. Unicode nativo == nvarchar, não varchar e como ANSI SQL exige uma conversão nos valores da tabela, não nos parâmetros, você obtém varreduras no índice em vez de buscas adequadas :(
O XML do seu plano Java confirma que a conversão implícita de VARCHAR para NVARCHAR está afetando o plano: