JOIN que multiplica a soma

Por que o total sobe depois de juntar pedidos com itens, como ver isso acontecendo e três jeitos de corrigir.

  • SQL
  • testado no PostgreSQL 16
  • exemplo para copiar

Publicado em

O problema

Você tem uma tabela de pedidos, com o valor de cada pedido, e uma tabela de itens, com uma linha para cada item comprado. Um pedido pode ter vários itens: é uma relação de 1 para muitos.

Quando você junta as duas com JOIN e soma o valor do pedido, cada pedido aparece uma vez para cada item. A soma infla sem que ninguém tenha vendido nada a mais.

Todos os números deste guia são de um exemplo fictício.

Monte o exemplo

Dois pedidos: o pedido 1 vale 100 e tem 3 itens; o pedido 2 vale 50 e tem 1 item.

SQL
CREATE TABLE pedidos (id int PRIMARY KEY, valor int);
CREATE TABLE itens (pedido_id int);
INSERT INTO pedidos VALUES (1, 100), (2, 50);
INSERT INTO itens VALUES (1), (1), (1), (2);

Veja a soma inflar

A soma certa dos pedidos:

SQL, resultado 150
SELECT sum(valor) FROM pedidos;

A mesma soma depois do JOIN com os itens:

SQL, resultado 350
SELECT sum(p.valor)
FROM pedidos p
JOIN itens i ON i.pedido_id = p.id;

O pedido de 100 aparece três vezes, uma por item: 100 + 100 + 100 + 50 = 350.

Por que acontece

A documentação do PostgreSQL, na seção 7.2.1.1 Joined Tables, explica que o JOIN gera uma linha para cada linha da outra tabela que satisfaz a condição. Se um pedido combina com três itens, ele vira três linhas.

O JOIN está fazendo o que deve. O erro é somar uma coluna do lado "1" depois de multiplicar as linhas pelo lado "muitos".

Correção 1: agregue os itens antes do JOIN

Resuma a tabela de itens para ter uma linha por pedido e só depois faça o JOIN.

SQL, resultado 150
SELECT sum(p.valor)
FROM pedidos p
JOIN (
  SELECT pedido_id, count(*) AS qtd_itens
  FROM itens
  GROUP BY pedido_id
) a ON a.pedido_id = p.id;

Use este caminho quando precisar de informação dos itens no resultado, como a quantidade por pedido.

Correção 2: só quer saber se tem item? Use EXISTS

Se a pergunta é apenas "este pedido tem algum item?", não precisa juntar as tabelas. O EXISTS testa a condição sem repetir o pedido.

SQL, resultado 150
SELECT sum(valor)
FROM pedidos p
WHERE EXISTS (
  SELECT 1 FROM itens i WHERE i.pedido_id = p.id
);

Confira as linhas antes e depois

Antes de confiar numa soma depois de um JOIN, compare a quantidade de linhas:

SQL, resultados 2 e 4
SELECT count(*) FROM pedidos;

SELECT count(*)
FROM pedidos p
JOIN itens i ON i.pedido_id = p.id;

Se você esperava uma linha por pedido e o número subiu, alguma linha foi multiplicada.

Pedido sem item? LEFT JOIN com os itens agregados

Agora entra um pedido 3, de valor 20, sem nenhum item. A soma certa passa a ser 170.

SQL
INSERT INTO pedidos VALUES (3, 20);

O JOIN comum e o EXISTS deixam o pedido 3 de fora e dão 150. O LEFT JOIN mantém todos os pedidos, mas precisa continuar com os itens agregados:

SQL, resultado 170
SELECT sum(p.valor)
FROM pedidos p
LEFT JOIN (
  SELECT pedido_id, count(*) AS qtd_itens
  FROM itens
  GROUP BY pedido_id
) a ON a.pedido_id = p.id;

Atenção

Trocar só o JOIN por LEFT JOIN, sem agregar os itens, dá 370. O LEFT JOIN resolve o pedido sem item, mas não resolve a multiplicação.

Antes de levar para o seu banco

  • Todos os resultados deste guia foram testados no PostgreSQL 16.15 em 07/11/2026.
  • A sintaxe de JOIN faz parte do padrão SQL, mas a própria documentação do PostgreSQL avisa, na seção 7.2.2 The WHERE Clause, que ela pode não ser tão portável para outros bancos. Teste no seu antes de usar em produção.
  • Os nomes das tabelas e os valores são fictícios. Troque pelos do seu modelo.

Fonte

Documentação oficial do PostgreSQL 16, página Table Expressions, seções 7.2.1.1 Joined Tables e 7.2.2 The WHERE Clause: postgresql.org/docs/16/queries-table-expressions.html. Conferida em 07/11/2026.