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.
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:
SELECT sum(valor) FROM pedidos;A mesma soma depois do JOIN com os itens:
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.
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.
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:
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.
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:
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.