Uma dúvida comum entre desenvolvedores e analistas de dados é:

Qual a diferença entre EXISTS, IN e JOIN?

Essas três abordagens podem retornar o mesmo resultado, mas possuem diferenças importantes de comportamento e performance, especialmente em bases grandes.

Para quem trabalha com ERP como o Protheus, onde as tabelas crescem rapidamente, entender essas diferenças ajuda a evitar consultas lentas e escolhas ruins de modelagem SQL.

Neste artigo, vamos analisar:

  • como cada abordagem funciona
  • quando usar cada uma
  • diferenças práticas de performance
  • exemplos com tabelas comuns do Protheus

Exemplo prático

Imagine duas tabelas comuns em um cenário de ERP:

  • SA1: cadastro de clientes
  • SC5: cabeçalho de pedidos de venda

Queremos responder à seguinte pergunta:

Quais clientes possuem pedidos cadastrados?


1. Usando JOIN

Uma forma bastante comum é utilizar JOIN.

SELECT DISTINCT A1_COD, A1_NOME
FROM SA1
JOIN SC5
    ON SC5.C5_CLIENTE = SA1.A1_COD
WHERE SA1.D_E_L_E_T_ = ''
  AND SC5.D_E_L_E_T_ = ''

O que acontece aqui

O banco faz a junção entre SA1 e SC5.

Se um cliente tiver vários pedidos, ele poderá aparecer várias vezes no resultado. Por isso, neste caso, usamos DISTINCT para eliminar duplicidades.

Vantagens

  • permite retornar dados das duas tabelas
  • tem sintaxe bastante conhecida
  • é uma boa escolha quando a relação entre os dados precisa ser exibida no resultado

Desvantagens

  • pode gerar duplicidade de registros
  • DISTINCT pode aumentar o custo da consulta
  • nem sempre é a melhor opção quando você só quer verificar existência

2. Usando IN

Outra forma comum é usar IN.

SELECT A1_COD, A1_NOME
FROM SA1
WHERE A1_COD IN (
    SELECT C5_CLIENTE
    FROM SC5
    WHERE D_E_L_E_T_ = ''
)
  AND SA1.D_E_L_E_T_ = ''

Como funciona

A subquery executa e retorna uma lista de clientes que possuem pedidos. Depois disso, o banco compara A1_COD com essa lista.

Vantagens

  • sintaxe simples
  • fácil de entender e manter
  • funciona bem quando a subconsulta retorna poucos valores

Possível problema

Se a subquery retornar muitos registros, essa abordagem pode ficar mais custosa, dependendo do otimizador e do banco utilizado.


3. Usando EXISTS

Agora a terceira abordagem.

SELECT A1_COD, A1_NOME
FROM SA1 A
WHERE A.D_E_L_E_T_ = ''
  AND EXISTS (
      SELECT 1
      FROM SC5 C
      WHERE C.C5_CLIENTE = A.A1_COD
        AND C.D_E_L_E_T_ = ''
  )

Como funciona

Para cada cliente em SA1, o banco verifica se existe ao menos um registro relacionado em SC5.

Assim que encontra o primeiro pedido correspondente, a busca pode ser encerrada.

Vantagens

  • geralmente escala melhor em grandes volumes
  • evita materializar listas desnecessárias
  • não gera duplicidade no resultado final
  • costuma ser a melhor opção quando o objetivo é apenas verificar existência

Diferença mental entre IN e EXISTS

Uma forma simples de memorizar:

AbordagemPergunta que ela responde
INO valor está dentro desta lista?
EXISTSExiste algum registro relacionado?

Exemplos:

  • cliente IN (lista de clientes com pedidos)
  • EXISTS (pedido onde cliente_id = cliente.id)

Exemplo em uma rotina AdvPL

Em uma rotina AdvPL utilizando SQL, você poderia montar a consulta assim com IN:

cQuery := "SELECT A1_COD, A1_NOME " + ;
          "FROM SA1 " + ;
          "WHERE A1_COD IN ( " + ;
          "    SELECT C5_CLIENTE " + ;
          "    FROM SC5 " + ;
          "    WHERE D_E_L_E_T_ = '' " + ;
          ") " + ;
          "AND SA1.D_E_L_E_T_ = '' "

Versão alternativa utilizando EXISTS:

cQuery := "SELECT A1_COD, A1_NOME " + ;
          "FROM SA1 A " + ;
          "WHERE A.D_E_L_E_T_ = '' " + ;
          "AND EXISTS ( " + ;
          "    SELECT 1 " + ;
          "    FROM SC5 C " + ;
          "    WHERE C.C5_CLIENTE = A.A1_COD " + ;
          "      AND C.D_E_L_E_T_ = '' " + ;
          ") "

Em bases grandes de ERP, essa troca pode fazer diferença perceptível no tempo de resposta.


Comparação rápida

MétodoMelhor uso
JOINQuando você precisa retornar dados das duas tabelas
INQuando trabalha com listas pequenas ou subconsultas simples
EXISTSQuando quer apenas verificar existência

Regra prática

Uma regra bastante usada no dia a dia é:

  • use JOIN quando precisar combinar e retornar colunas de duas tabelas
  • use EXISTS quando quiser apenas saber se há relacionamento
  • use IN quando estiver lidando com listas pequenas ou valores fixos

Dica para quem trabalha com ERP

Em sistemas ERP, as tabelas podem crescer para milhões de registros. Nesses cenários:

  • EXISTS costuma escalar melhor
  • JOIN pode multiplicar linhas e exigir DISTINCT
  • IN pode se tornar menos eficiente com listas muito grandes

Sempre que possível, analise o plano de execução da consulta antes de assumir qual versão será a melhor.


Conclusão

EXISTS, IN e JOIN podem produzir o mesmo resultado em alguns cenários, mas não são equivalentes.

Resumo prático:

  • JOIN quando você precisa trazer dados relacionados
  • IN quando a comparação é com listas pequenas
  • EXISTS quando a intenção é apenas validar existência

Conhecer essas diferenças ajuda a escrever SQL mais eficiente, previsível e escalável, principalmente em ambientes como o Protheus.


Leituras recomendadas

Se você trabalha com SQL no dia a dia, vale aprofundar também nestes temas:

  • NOT EXISTS vs NOT IN
  • LEFT JOIN vs NOT EXISTS
  • GROUP BY vs DISTINCT