Uma venda guarda o identificador do cliente, e não o nome dele, e cada item da venda guarda o identificador do produto, e não a descrição. Para ver a venda completa, com o nome do cliente e a descrição dos produtos, a consulta precisa juntar as tabelas. A forma da junção decide quais linhas aparecem, e uma junção mal escrita esconde vendas ou multiplica valores.
Por que os dados ficam separados
Cada informação fica guardada em um único lugar. O nome do cliente fica na tabela clientes, e as vendas desse cliente apenas apontam para ele. Assim, uma correção no nome do cliente vale para todas as vendas, sem a necessidade de alterar cada uma delas. No cenário, as ligações entre as tabelas são estas:
- a coluna
cliente_idda tabelavendasaponta para a colunaidda tabelaclientes; - a coluna
venda_idda tabelaitens_vendaaponta para a colunaidda tabelavendas; - a coluna
produto_idda tabelaitens_vendaaponta para a colunaidda tabelaprodutos.
A junção comum
A junção comum, escrita com JOIN, combina cada linha de uma tabela com as linhas da outra que cumprem a condição escrita depois da palavra ON:
SELECT v.numero_documento, c.nome, v.total FROM vendas v JOIN clientes c ON c.id = v.cliente_id WHERE v.numero_documento = 4521;
As letras v e c são apelidos das tabelas, escritos logo depois do nome de cada uma. Com os apelidos, a consulta indica de qual tabela vem cada coluna, como em v.total e c.nome.
A junção comum mantém apenas as linhas que encontram correspondência nas duas tabelas. No cenário, a venda de um consumidor que não se identificou tem a coluna cliente_id sem valor, em NULL, e por isso não encontra nenhum cliente. Essa venda desaparece do resultado da junção comum.
A junção que preserva as linhas
A junção à esquerda, escrita com LEFT JOIN, mantém todas as linhas da primeira tabela, mesmo as que não encontram correspondência na segunda. Nessas linhas, as colunas da segunda tabela aparecem em NULL:
SELECT v.numero_documento, c.nome, v.total FROM vendas v LEFT JOIN clientes c ON c.id = v.cliente_id;: mostra todas as vendas, e as vendas sem cliente aparecem com o nome em NULL.
A mesma junção encontra as linhas sem correspondência. Para listar os clientes que nunca compraram, a consulta mantém apenas os clientes cuja venda correspondente não existe:
SELECT c.id, c.nome FROM clientes c LEFT JOIN vendas v ON v.cliente_id = c.id WHERE v.id IS NULL;
Juntar três tabelas
As junções podem ser encadeadas. Para ver os produtos de uma venda, a consulta parte da venda, passa pelos itens e chega aos produtos:
SELECT v.numero_documento, p.descricao, i.quantidade, i.preco_unitario FROM vendas v JOIN itens_venda i ON i.venda_id = v.id JOIN produtos p ON p.id = i.produto_id WHERE v.numero_documento = 4521;
O resultado mostra uma linha para cada item da venda.
As linhas multiplicadas
Uma junção repete os dados de uma tabela para cada correspondência encontrada na outra. Uma venda com cinco itens aparece cinco vezes na junção com a tabela itens_venda, e essa repetição muda os resumos:
SUM(v.total)soma o total de cada venda uma vez por item, e o resultado fica muito maior que o faturamento real;COUNT(*)conta os itens, e não as vendas;COUNT(DISTINCT v.id)conta cada venda uma única vez.
Uma junção sem a condição ON, ou com a condição errada, combina todas as linhas de uma tabela com todas as linhas da outra. Duas tabelas de dez mil linhas produzem cem milhões de combinações, e a consulta pode ocupar o servidor por muito tempo.
Os registros sem par
A junção também revela linhas que apontam para algo que não existe mais, como um item de venda cujo produto foi apagado. Essas linhas são chamadas de registros órfãos:
SELECT i.id, i.produto_id FROM itens_venda i LEFT JOIN produtos p ON p.id = i.produto_id WHERE p.id IS NULL;: mostra os itens que apontam para produtos inexistentes.
No suporte
Quando uma consulta com junção traz menos linhas que o esperado, a primeira suspeita é a junção comum, que descarta as linhas sem par. Quando um total sai maior que o esperado, a suspeita é a repetição de linhas causada pela junção. Em consultas grandes, convém conferir a condição ON antes de executar.
Para guardar
- Os dados ficam em tabelas separadas, ligadas por colunas que apontam para o
idde outra tabela. - A junção comum mantém apenas as linhas com correspondência nas duas tabelas.
- O LEFT JOIN mantém todas as linhas da primeira tabela e completa a falta de par com NULL.
- A junção repete linhas e pode multiplicar somas e contagens.
- Uma junção sem condição combina todas as linhas com todas as linhas.
A consulta que junta vendas e clientes com JOIN devolveu menos vendas do que existem na tabela vendas. Por quê?
A junção comum mantém apenas as vendas que encontram um cliente correspondente. As vendas sem cliente identificado, com a coluna cliente_id em NULL, ficam de fora. Com LEFT JOIN, todas as vendas aparecem, e as colunas do cliente vêm em NULL quando não há correspondência.
Depois de juntar vendas e itens_venda, a soma de v.total ficou muito maior que o faturamento real. Qual é a causa?
A junção repete os dados de cada venda em uma linha para cada item. A soma de v.total conta o total de cada venda tantas vezes quanto ela tem itens. O faturamento deve ser somado na tabela vendas, sem a junção com os itens, ou calculado a partir dos valores dos próprios itens.