Expansão de junções e multiplicação de linhas
Entenda por que uma junção pode retornar mais linhas do que qualquer uma das tabelas e como entrevistadores testam isso.
Expansão de junções e multiplicação de linhas é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.
Quando um JOIN retorna linhas demais
Uma das perguntas de entrevista mais reveladoras parece inocente: "uma junção pode retornar mais linhas do que a tabela maior?" A resposta é sim, e o fenômeno é chamado de expansão ou multiplicação de linhas.
Candidatos que dizem "uma junção apenas combina tabelas" não percebem isso. Candidatos que conseguem prever a quantidade exata de linhas são contratados. Esta lição desenvolve essa capacidade de previsão.
A causa: correspondências um-para-muitos
A expansão acontece quando uma linha à esquerda corresponde a várias linhas à direita. Cada correspondência produz uma linha de saída separada.
Com clientes e pedidos, Ada (uma cliente) tem dois pedidos. A junção gera uma linha por pedido, portanto Ada aparece duplicada. Os campos do cliente se repetem; apenas os campos do pedido diferem.
SELECT c.name, o.amount
FROM customers c
JOIN orders o ON o.customer_id = c.id;
-- Ada appears twice (she has 2 orders)
-- name | amount
-- Ada | 50
-- Ada | 20
-- Bob | 99Contando as linhas de saída
A quantidade de linhas de saída é igual à soma das correspondências por linha da esquerda, não à quantidade de clientes.
- Ada -> 2 pedidos -> 2 linhas
- Bob -> 1 pedido -> 1 linha
- Cleo -> 0 pedidos -> 0 linhas (removida pelo INNER JOIN)
Total = 3 linhas, embora a tabela de clientes também tenha 3 linhas. Se Ada passar a ter 10 pedidos, o resultado saltará para 11 linhas.
Muitos-para-muitos explode
A expansão se intensifica quando os dois lados têm várias correspondências para a mesma chave. Se a chave K aparece 3 vezes à esquerda e 4 vezes à direita, a junção produz 3 x 4 = 12 linhas para essa chave.
É assim que uma junção aparentemente pequena cresce para milhões de linhas. Os entrevistadores adoram fornecer chaves duplicadas nos dois lados para verificar se você percebe a multiplicação.
-- left has 3 rows with tag 'A', right has 4 rows with tag 'A'
SELECT l.id, r.id
FROM left_t l
JOIN right_t r ON r.tag = l.tag;
-- tag 'A' alone yields 3 * 4 = 12 output rowsA armadilha da agregação
Este é o erro que os entrevistadores mais costumam preparar. Você faz uma junção entre pedidos e itens do pedido para obter os detalhes dos itens e depois calcula o SUM do valor do pedido. Como cada pedido se expande em várias linhas de itens, o valor do pedido é contado uma vez por item.
O SUM fica muito inflado. A consulta parece correta e até é executada, o que a torna perigosa.
-- BUG: order.amount duplicated across items
SELECT SUM(o.amount) AS total
FROM orders o
JOIN order_items i ON i.order_id = o.id;
-- a 3-item order counts o.amount 3 timesObservando a inflação
Suponha que um pedido tenha valor 100 e três itens. A junção produz três linhas, cada uma carregando o valor 100. SUM(o.amount) retorna 300, não 100.
A solução é agregar na granularidade correta: some os itens ou some os pedidos distintos separadamente. Nunca calcule o SUM de um valor do registro pai sobre uma junção expandida com registros filhos.
o.id | o.amount | i.id
7 | 100 | 71
7 | 100 | 72
7 | 100 | 73
-- SUM(o.amount) = 300 (WRONG, should be 100)Correção 1: agregue primeiro os filhos
A correção mais limpa é pré-agregar o lado com muitos registros em uma subconsulta ou CTE, para que cada registro pai corresponda exatamente a uma linha resumida. Sem expansão, sem inflação.
Aqui, reduzimos os itens a uma linha por pedido antes da junção, portanto o valor do registro pai nunca é duplicado.
SELECT o.id, o.amount, i.item_count
FROM orders o
JOIN (
SELECT order_id, COUNT(*) AS item_count
FROM order_items
GROUP BY order_id
) i ON i.order_id = o.id;Correção 2: COUNT(DISTINCT) e somas condicionais
Se precisar agregar depois de uma junção expandida, conte ou some na granularidade correta. Use COUNT(DISTINCT o.id) para contar pedidos em vez de linhas de itens.
Observação: SUM(DISTINCT o.amount) NÃO é uma correção segura, porque dois pedidos diferentes podem legitimamente ter o mesmo valor e seriam combinados. A pré-agregação é mais confiável.
SELECT COUNT(DISTINCT o.id) AS num_orders,
COUNT(i.id) AS num_items
FROM orders o
JOIN order_items i ON i.order_id = o.id;Detectando a expansão antes que cause problemas
Um diagnóstico rápido de que os entrevistadores gostam: verifique se a chave da junção é única no lado que você espera que seja o "um". Se a quantidade de chaves distintas for menor que a quantidade de linhas, esse lado tem duplicatas e causará expansão.
-- if this returns rows, order_id is NOT unique in order_items
SELECT order_id, COUNT(*) AS n
FROM order_items
GROUP BY order_id
HAVING COUNT(*) > 1;Verificando sua granularidade com uma contagem
Antes de confiar em qualquer agregação sobre um resultado combinado, verifique a quantidade de linhas. Um truque rápido é comparar a contagem da junção com a contagem da tabela que você espera que defina a granularidade.
Se COUNT(*) sobre a junção for maior que COUNT(*) de pedidos, a junção se expandiu e qualquer agregação por pedido está em risco. Esta verificação de uma linha já salvou muitas respostas em entrevistas.
-- joined rows should equal order count if no fan-out
SELECT COUNT(*) AS joined_rows
FROM orders o
JOIN order_items i ON i.order_id = o.id;
SELECT COUNT(*) AS order_rows FROM orders;
-- joined_rows > order_rows => fan-out presentA expansão nem sempre é um erro
Às vezes, você quer uma linha por filho. Listar cada item do pedido com o cabeçalho do pedido é uma expansão correta. A habilidade está em conhecer sua granularidade: quantas linhas uma entidade deve produzir?
Declare a granularidade antes de escrever a consulta. "Quero uma linha por item do pedido" em comparação com "uma linha por pedido" determina se a expansão é um recurso ou um erro.
Verificação rápida
Preveja a saída de uma junção um-para-muitos.
Recapitulação: expansão e multiplicação de linhas
O que você deve lembrar:
- Um JOIN gera uma linha por par correspondente, portanto as correspondências um-para-muitos duplicam o lado "um".
- Chaves muitos-para-muitos se multiplicam: 3 x 4 = 12 linhas para essa chave.
- Agregar um valor do registro pai sobre uma junção expandida infla somas e contagens.
- Corrija isso pré-agregando os filhos ou contando/somando na granularidade correta (por exemplo,
COUNT(DISTINCT)). - Sempre declare primeiro a granularidade desejada; a expansão só é um erro quando a viola.
Perguntas Frequentes
A aula “Expansão de junções e multiplicação de linhas” é grátis?
Sim — o texto completo de “Expansão de junções e multiplicação de linhas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.
O que vou aprender em “Expansão de junções e multiplicação de linhas”?
Entenda por que uma junção pode retornar mais linhas do que qualquer uma das tabelas e como entrevistadores testam isso. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Coding Interview Prep?
Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Expansão de junções e multiplicação de linhas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Coding Interview Prep?
Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Como INNER JOIN faz correspondência entre linhas
- ON versus WHERE em junções
- Expansão de junções e multiplicação de linhas
- Unindo três ou mais tabelas