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”

