| Bellacosa Mainframe e o uso dos sqljoins em SQL |
☕ Um Café no Bellacosa Mainframe
SQL JOINs: O Que Todo Programador COBOL Padawan Precisa Saber Para Entender Como os Bancos de Dados Pensam
Você não está apenas aprendendo comandos SQL. Está aprendendo como bilhões de registros conversam entre si todos os dias.
Existem momentos na carreira de um programador em que um conceito muda completamente sua forma de enxergar a computação.
Para alguns, esse momento acontece ao aprender ponteiros em C.
Para outros, ao descobrir orientação a objetos.
Para quem trabalha com bancos de dados relacionais, esse momento geralmente acontece quando entende, de verdade, os JOINs.
À primeira vista, parecem apenas quatro comandos:
INNER JOIN
LEFT JOIN
RIGHT JOIN
FULL OUTER JOIN
Mas, por trás deles, existe uma das maiores invenções da Ciência da Computação moderna.
Para um programador COBOL, compreender JOINs é semelhante a descobrir que, em vez de navegar manualmente por dezenas de arquivos VSAM procurando registros relacionados, existe um mecanismo matemático capaz de fazer isso automaticamente, de forma otimizada, segura e extremamente rápida.
Pegue seu café.
Hoje vamos conversar sobre como os bancos de dados realmente pensam.
Antes do SQL
Imagine que estamos em 1975.
Você trabalha em um grande banco.
Os dados estão espalhados em arquivos.
Existe um arquivo de clientes.
Outro de contas.
Outro de cartões.
Outro de empréstimos.
Outro de movimentações.
Em COBOL, o processamento normalmente seria algo parecido com:
Ler Cliente
Para cada Cliente
Abrir arquivo de Contas
Procurar Conta
Abrir arquivo de Movimentos
Procurar Movimentos
Gerar relatório
Nada de errado.
Foi assim durante décadas.
Mas existe um problema.
À medida que os arquivos crescem...
...o processamento cresce junto.
Às vezes de forma exponencial.
Era necessário pensar diferente.
Surge o Modelo Relacional
Em 1970, Edgar F. Codd publicou um artigo que mudou a história da informática.
Sua ideia era brilhante.
Em vez de navegar manualmente entre arquivos...
...o programador apenas declararia:
"Quero relacionar estas duas tabelas."
O banco descobriria o melhor caminho.
Foi o nascimento do SQL moderno.
E junto dele...
...os JOINs.
O que é um JOIN?
JOIN significa literalmente
Junção.
É a operação que une informações de duas ou mais tabelas utilizando algum relacionamento.
Imagine duas tabelas.
CLIENTE
| ID | Nome |
|---|---|
| 1 | João |
| 2 | Maria |
| 3 | Carlos |
| 4 | Ana |
PEDIDO
| Pedido | Cliente | Valor |
|---|---|---|
| 101 | 1 | 250 |
| 102 | 1 | 400 |
| 103 | 3 | 120 |
| 104 | 5 | 900 |
Observe.
O pedido possui um campo chamado CLIENTE.
Esse campo aponta para o ID da tabela CLIENTE.
Visualmente:
CLIENTE
1 João
2 Maria
3 Carlos
4 Ana
│
▼
PEDIDO
101 Cliente 1
102 Cliente 1
103 Cliente 3
104 Cliente 5
Existe um relacionamento.
É justamente isso que o JOIN explora.
INNER JOIN
É o JOIN mais famoso.
Ele responde uma pergunta muito simples.
"Quais registros existem nos dois lados?"
SQL
SELECT
C.NOME,
P.VALOR
FROM CLIENTE C
INNER JOIN PEDIDO P
ON C.ID = P.CLIENTE_ID;
Resultado
| Cliente | Valor |
|---|---|
| João | 250 |
| João | 400 |
| Carlos | 120 |
Perceba.
Maria desapareceu.
Ana desapareceu.
O pedido do cliente 5 também desapareceu.
Por quê?
Porque não existe correspondência.
O INNER JOIN trabalha exclusivamente com a interseção.
Imagine dois círculos.
CLIENTES
(1 2 3 4)
PEDIDOS
(1 3 5)
Resultado
(1 3)
Apenas aquilo que existe em ambos.
A mágica acontece automaticamente
Quando você escreve:
INNER JOIN
Você não está dizendo ao banco:
"Leia primeiro esta tabela."
Nem:
"Depois percorra aquela."
Nem:
"Faça um loop."
Você apenas declara o resultado desejado.
O banco escolhe como chegar nele.
Esse é o paradigma declarativo.
LEFT JOIN
Agora imagine outra pergunta.
"Quero todos os clientes."
Mesmo aqueles que nunca compraram.
É aqui que entra o LEFT JOIN.
SELECT
C.NOME,
P.VALOR
FROM CLIENTE C
LEFT JOIN PEDIDO P
ON C.ID=P.CLIENTE_ID;
Resultado
| Cliente | Valor |
|---|---|
| João | 250 |
| João | 400 |
| Maria | NULL |
| Carlos | 120 |
| Ana | NULL |
Observe o NULL.
Ele não significa zero.
Não significa vazio.
Significa simplesmente:
"Não existe registro correspondente."
Esse pequeno detalhe causa milhares de erros em sistemas corporativos.
Descobrindo clientes inativos
Um dos usos mais comuns do LEFT JOIN.
SELECT C.*
FROM CLIENTE C
LEFT JOIN PEDIDO P
ON C.ID=P.CLIENTE_ID
WHERE P.CLIENTE_ID IS NULL;
Resultado.
Maria.
Ana.
São clientes cadastrados.
Mas nunca fizeram pedidos.
Esse tipo de consulta é extremamente comum em:
CRM
Bancos
Seguradoras
Telecom
E-commerce
RIGHT JOIN
É simplesmente o espelho do LEFT.
Mantém todos os registros da direita.
Na prática...
Poucos desenvolvedores utilizam RIGHT JOIN.
A maioria prefere inverter as tabelas e continuar usando LEFT JOIN.
É mais intuitivo.
FULL OUTER JOIN
Esse é o mais democrático.
Nada é descartado.
Tudo aparece.
Clientes com pedidos.
Clientes sem pedidos.
Pedidos sem clientes.
Tudo.
CLIENTES
1
2
3
4
PEDIDOS
1
3
5
Resultado
1
2
3
4
5
Muito utilizado em auditorias.
Migração de sistemas.
Comparação de bases.
Validação de integrações.
O papel do NULL
Uma das maiores dificuldades dos iniciantes.
Imagine.
Maria nunca comprou.
O resultado será
Maria
NULL
Não escreva
WHERE VALOR=0
Porque NULL não é zero.
Use
WHERE VALOR IS NULL
Parece detalhe.
Mas não é.
O problema da multiplicação de registros
Esse é um conceito que surpreende muitos programadores COBOL.
Imagine.
Um cliente.
Cinco pedidos.
CLIENTE
João
PEDIDO
101
102
103
104
105
Resultado do JOIN.
João
101
João
102
João
103
João
104
João
105
Um registro virou cinco.
Isso é perfeitamente normal.
É consequência da cardinalidade.
Cardinalidade
Existem quatro situações clássicas.
Um para Um
Pessoa
↓
CPF
Cada pessoa possui apenas um CPF.
Um para Muitos
Cliente
↓
Pedidos
Um cliente possui vários pedidos.
É o relacionamento mais comum.
Muitos para Um
Milhares de pedidos.
↓
Um cliente.
É apenas a visão inversa.
Muitos para Muitos
Aluno
↓
Disciplina
Um aluno cursa várias disciplinas.
Uma disciplina possui vários alunos.
Nesse caso normalmente existe uma terceira tabela intermediária.
O erro que derruba servidores
Todo DBA conhece essa história.
Um desenvolvedor escreve:
SELECT *
FROM CLIENTE,
PEDIDO;
Ou
JOIN
sem ON
Resultado.
Produto Cartesiano.
Se houver:
100.000 clientes
100.000 pedidos
O banco produzirá
10 bilhões de combinações.
Pode consumir CPU, memória e I/O de forma devastadora.
Por isso, nunca execute um JOIN sem uma condição de relacionamento bem definida.
Como o DB2 realmente executa um JOIN?
Aqui entramos no mundo do IBM Mainframe.
Você escreve SQL.
Mas quem decide o caminho é o Otimizador do DB2.
Ele analisa dezenas de fatores.
Quantidade de registros.
Índices.
Estatísticas.
Cardinalidade.
Distribuição dos valores.
Buffer Pool.
Espaço disponível.
Custo estimado.
No final...
Escolhe um plano de execução.
É semelhante ao GPS.
Você informa o destino.
Ele calcula a melhor rota.
Nested Loop Join
Imagine duas listas telefônicas.
Você pega um nome.
Procura na outra lista.
Repete.
Esse é o Nested Loop.
Cliente
↓
Índice
↓
Pedido
Excelente quando existe índice.
Muito utilizado em consultas seletivas.
Merge Join
Agora imagine duas listas ordenadas alfabeticamente.
Você percorre ambas ao mesmo tempo.
Sem voltar.
Sem pesquisar novamente.
Muito eficiente para grandes conjuntos já classificados.
Hash Join
Agora imagine uma tabela hash.
Os registros menores são colocados em memória.
Depois a outra tabela apenas consulta essa estrutura.
É extremamente rápido para determinadas situações.
O papel dos índices
Sem índices...
O banco precisa procurar registro por registro.
Com índices...
Ele encontra rapidamente os dados desejados.
Imagine um livro de 2.000 páginas.
Sem índice.
Você folheia página por página.
Com índice.
Vai diretamente ao assunto.
É exatamente isso que acontece.
RUNSTATS
No DB2 existe um utilitário extremamente importante.
RUNSTATS.
Ele atualiza as estatísticas do banco.
Quantidade de linhas.
Número de páginas.
Distribuição dos valores.
Percentual de registros.
Sem essas informações...
O otimizador pode escolher um caminho ruim.
É como dirigir sem GPS.
EXPLAIN
Todo desenvolvedor Mainframe deveria aprender EXPLAIN.
Ele responde perguntas como:
Qual índice será utilizado?
Quantas páginas serão lidas?
Haverá Tablespace Scan?
Será usado Hash Join?
Nested Loop?
Merge Join?
O EXPLAIN é uma janela para o cérebro do DB2.
Não basta escrever SQL correto.
É preciso entender como ele será executado.
JOIN não é apenas SQL
Muitos iniciantes acreditam que JOIN pertence exclusivamente ao SQL.
Na verdade...
JOIN é um conceito matemático.
Baseado em Álgebra Relacional.
Projetos.
Seleções.
Uniões.
Diferenças.
Interseções.
Produto Cartesiano.
Todos esses operadores são estudados muito antes do SQL existir.
SQL apenas transformou essas ideias em uma linguagem prática.
A evolução para Big Data
Mesmo tecnologias modernas continuam utilizando o conceito de JOIN.
Spark SQL.
Hive.
Snowflake.
BigQuery.
Databricks.
DuckDB.
PostgreSQL.
Oracle.
SQL Server.
Todos executam JOINs.
Mudam os algoritmos.
Mudam as otimizações.
Mas a teoria continua exatamente a mesma criada por Edgar Codd há mais de cinquenta anos.
Isso demonstra a força de uma boa ideia.
O que um Programador COBOL Padawan deve levar desta conversa?
Se você está começando sua jornada em informática, talvez pense que JOIN é apenas mais uma palavra da linguagem SQL.
Não é.
JOIN representa uma mudança de mentalidade.
No mundo procedural, típico de muitos programas COBOL tradicionais, você descreve passo a passo como localizar, ler e combinar registros. No mundo relacional, você descreve o que deseja obter, e o banco de dados decide a melhor estratégia para alcançar esse resultado.
Essa diferença parece sutil, mas muda completamente a forma de projetar sistemas.
Ao dominar JOINs, você deixa de enxergar tabelas como arquivos isolados e passa a vê-las como partes de uma grande rede de relacionamentos. É essa visão que permite construir consultas elegantes, sistemas escaláveis e aplicações capazes de responder rapidamente a perguntas complexas sobre milhões — ou até bilhões — de registros.
Da próxima vez que escrever um INNER JOIN, lembre-se de que não está apenas unindo duas tabelas. Está utilizando um dos conceitos mais elegantes da Álgebra Relacional, aperfeiçoado por décadas de pesquisa e otimizado continuamente pelos maiores bancos de dados do mundo.
E essa é uma das grandes lições da engenharia de software: as tecnologias evoluem, as linguagens mudam, o hardware se transforma, mas os fundamentos permanecem. Quem aprende esses fundamentos não está apenas estudando SQL; está construindo uma base sólida para compreender qualquer plataforma de dados, do IBM Z aos ambientes distribuídos em nuvem.
No Bellacosa Mainframe, costumamos dizer que um verdadeiro COBOL Padawan não coleciona apenas comandos. Ele coleciona maneiras diferentes de pensar. Porque, no fim das contas, programar nunca foi apenas escrever código. Programar é aprender a conversar com a lógica que organiza o mundo dos dados. E os JOINs são uma das linguagens mais poderosas dessa conversa.