Home Blog A seguir está um guia completo e profissional de Performance Tuning com foco exclusivo em conversões implícitas no SQL Server, usando exemplos reais do banco AdventureWorks (AdventureWorks2019).

A seguir está um guia completo e profissional de Performance Tuning com foco exclusivo em conversões implícitas no SQL Server, usando exemplos reais do banco AdventureWorks (AdventureWorks2019).

Este é um dos maiores vilões ocultos de performance — e muita gente nem sabe que está causando lentidão.


🚀 Performance Tuning focado em Conversões Implícitas – SQL Server + AdventureWorks


🔍 1. O que são conversões implícitas?

O SQL Server converte automaticamente o tipo de dados de um lado da expressão quando ele não corresponde ao outro.

Exemplos clássicos:

  • Comparar NVARCHAR com INT
  • Comparar VARCHAR com DATETIME
  • Comparar VARCHAR(50) com VARCHAR(MAX)
  • Comparar DECIMAL(10,2) com FLOAT
  • Usar parâmetros da aplicação no tipo errado

Isso causa:
✔ Full scans desnecessários
✔ Não uso do índice
✔ Estouro de CPU
✔ Regressão de desempenho


🧨 2. Como as conversões implícitas afetam o desempenho?

Elas inviabilizam o uso de índices, pois obrigam o SQL Server a converter a coluna inteira para um tipo compatível.

Exemplo:

WHERE SalesOrderID = '50000'

Coluna → INT
Parâmetro → VARCHAR

Resultado: SCAN ao invés de SEEK.


🧪 3. Identificando conversões implícitas com AdventureWorks

📌 3.1. Consultando o Execution Plan

Rodando este comando no AdventureWorks2019:

SELECT *
FROM Sales.SalesOrderHeader
WHERE SalesOrderID = '43759';

Isso gera:

CONVERT_IMPLICIT(int, [SalesOrderID], 0)
📌 E transforma um seek em scan.

Você verá um Warning no plano.


📌 3.2. Localizando conversões implícitas usando DMV

Script oficial para achar conversões implícitas (Microsoft pattern):

SELECT 
    qs.sql_handle,
    qs.execution_count,
    qs.total_elapsed_time / qs.execution_count AS avg_time,
    qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qp.query_plan.value('(/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple/QueryPlan/RelOp//ScalarOperator/Convert[@Implicit="1"])[1]', 'varchar(100)') IS NOT NULL;

Esse script lista:
✔ Queries com conversão implícita
✔ Plano
✔ Tempo médio
✔ Frequência


🎯 4. Exemplos reais (AdventureWorks)

🔹 4.1 Problema: Comparação VARCHAR → INT

SELECT p.ProductID, p.Name
FROM Production.Product p
WHERE p.ProductID = '480';

❌ O SQL converte a coluna ProductID (INT) para VARCHAR
➡ SCAN

✔ Solução:

WHERE p.ProductID = 480;

🔹 4.2 Problema: Comparação DATETIME → VARCHAR

SELECT *
FROM Sales.SalesOrderHeader
WHERE OrderDate = '2013-01-31';

Dependendo do formato, SQL faz:

⚠ CONVERT_IMPLICIT(datetime, varchar(10))

✔ Solução ideal:

WHERE OrderDate = DATEFROMPARTS(2013,1,31);

ou usar parâmetro no tipo correto.


🔹 4.3 Problema: JOIN com conversões

SELECT *
FROM Sales.Customer c
JOIN Sales.SalesOrderHeader h
    ON c.CustomerID = CAST(h.CustomerID AS VARCHAR(10));

⚠ Conversão no JOIN
➡ SCAN
➡ SORT
➡ HASH JOIN desnecessário

✔ Solução:

JOIN Sales.SalesOrderHeader h
    ON c.CustomerID = h.CustomerID;

🔹 4.4 Problema: VARCHAR(MAX) prejudicando índice

SELECT *
FROM Person.Person
WHERE LastName = 'Anderson';

Se LastName for VARCHAR(50) e o parâmetro for VARCHAR(MAX):

➡ Convert_implicit(VARCHAR(50), VARCHAR(MAX))
➡ Table Scan

✔ Solução:

Garantir tamanho correto do parâmetro:

DECLARE @name VARCHAR(50) = 'Anderson';

SELECT *
FROM Person.Person
WHERE LastName = @name;

🧰 5. Checklist profissional para evitar conversões implícitas

✔ Parametrização correta na Aplicação

  • Sempre enviar parâmetros com tipos correspondentes ao do SQL Server
  • Se usar Delphi, configurar FieldDefs corretamente no FireDAC

✔ Ajustar queries

  • Nunca comparar coluna numérica com string
  • Nunca comparar datetime com varchar
  • Evitar conversões no WHERE:

WHERE CAST(Coluna AS VARCHAR) = '...'
✔ Mover CAST para o parâmetro, não para a coluna

✔ Ajustar tipos de dados inconsistentes

  • Colunas relacionadas (FKs) devem ter o mesmo tipo
  • Colunas de chave devem ter o mesmo comprimento
  • Evitar VARCHAR(MAX) em campos de pesquisa

✔ Usar tabelas temporárias ao invés de conversões

  • Criar colunas do tipo correto
  • Inserir dados consistentes

🛠 6. Script para localizar conversões implícitas em todas as consultas ativas

SELECT 
    t.text,
    qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qp.query_plan.exist('//*[@Implicit="1"]') = 1;

Esse pega todas as conversões implícitas encontradas dentro do XML do plano.


🧨 7. Script para detectar colunas com tipos diferentes entre FKs (AdventureWorks)

SELECT 
    f.name AS FK_Name,
    OBJECT_NAME(f.parent_object_id) AS TabelaFilha,
    c1.name AS ColunaFilha,
    ty1.name AS TipoFilha,
    OBJECT_NAME(f.referenced_object_id) AS TabelaPai,
    c2.name AS ColunaPai,
    ty2.name AS TipoPai
FROM sys.foreign_key_columns f
JOIN sys.columns c1 
    ON f.parent_object_id = c1.object_id 
    AND f.parent_column_id = c1.column_id
JOIN sys.types ty1 ON c1.user_type_id = ty1.user_type_id
JOIN sys.columns c2 
    ON f.referenced_object_id = c2.object_id 
    AND f.referenced_column_id = c2.column_id
JOIN sys.types ty2 ON c2.user_type_id = ty2.user_type_id
WHERE ty1.name <> ty2.name;

✔ Mostra inconsistências estruturais
✔ Indica onde surgem conversões implícitas externas


📌 8. Melhor prática definitiva

Sempre que for usar parâmetro:

Declare exatamente no tipo da coluna.

Exemplo no AdventureWorks:

DECLARE @OrderID INT = 43759;

SELECT *
FROM Sales.SalesOrderHeader
WHERE SalesOrderID = @OrderID;

🎁 Quer gerar um PDF com este conteúdo completo?

Posso montar um PDF profissonal, com formato de checklist, exemplos e scripts. Só pedir:
👉 “Gerar PDF das conversões implícitas”

related posts

Leave a Comment