DoesItHold

Por que um JOIN duplica linhas e como corrigir a cardinalidade

Um JOIN combina linhas dos dois lados. Quando um cliente tem muitos pedidos, um JOIN simples retorna uma linha por pedido, então o cliente aparece várias vezes e qualquer total por cliente fica errado.

A resposta rápida

Defina a granularidade desejada, isto é, o que cada linha deve representar. Para obter um total por cliente, agregue o lado de muitos com SUM e agrupe pelo cliente, em vez de esconder duplicatas com DISTINCT.

Um exemplo mínimo

Com bug: uma linha por pedido, então clientes se repetem
SELECT a.id, a.name, b.title
FROM authors a
JOIN books b ON b.author_id = a.id;
Corrigido: agregue os pedidos na granularidade de cliente
SELECT a.id, a.name, COUNT(b.id) AS book_count
FROM authors a
JOIN books b ON b.author_id = a.id
GROUP BY a.id, a.name;

A consulta com bug retorna uma linha para cada pedido; por isso, um cliente com dois pedidos aparece duas vezes. Agrupar por cliente e somar os pedidos produz uma linha correta para cada cliente.

Como depurar

  1. Declare a granularidade esperada: uma linha por cliente, por pedido ou por dia.
  2. Conte as linhas de cada lado do JOIN para uma chave conhecida.
  3. Se o resultado tem mais linhas que a granularidade pretendida, o JOIN está multiplicando linhas.
  4. Agregue o lado de muitos na granularidade pretendida em vez de mascarar linhas com DISTINCT.

Correções erradas comuns

  • Adicionar DISTINCT, que esconde duplicatas mas não soma nada e pode descartar linhas legitimamente iguais.
  • Usar LIMIT para fazer a contagem de linhas parecer certa sem corrigir o grão.
  • Fazer o JOIN por uma chave incompleta, de modo que algumas linhas casam por acaso.

Limites deste guia

Isto cobre o espalhamento um-para-muitos para um total por cliente. Bugs de cardinalidade também aparecem com joins muitos-para-muitos e predicados ausentes. O desafio foca no caso de espalhamento mostrado aqui.

Referências oficiais

  1. PostgreSQL — tabelas combinadas e GROUP BY
  2. PostgreSQL — SELECT e DISTINCT
  3. PostgreSQL — funções de agregação

Pronto para experimentar?

Corrigir este bug em um desafio grátis

Páginas relacionadas