Esquema :
CREATE TABLE "items" (
"id" SERIAL NOT NULL PRIMARY KEY,
"country" VARCHAR(2) NOT NULL,
"created" TIMESTAMP WITH TIME ZONE NOT NULL,
"price" NUMERIC(11, 2) NOT NULL
);
CREATE TABLE "payments" (
"id" SERIAL NOT NULL PRIMARY KEY,
"created" TIMESTAMP WITH TIME ZONE NOT NULL,
"amount" NUMERIC(11, 2) NOT NULL,
"item_id" INTEGER NULL
);
CREATE TABLE "extras" (
"id" SERIAL NOT NULL PRIMARY KEY,
"created" TIMESTAMP WITH TIME ZONE NOT NULL,
"amount" NUMERIC(11, 2) NOT NULL,
"item_id" INTEGER NULL
);
Dados :
INSERT INTO items VALUES
(1, 'CZ', '2016-11-01', 100),
(2, 'CZ', '2016-11-02', 100),
(3, 'PL', '2016-11-03', 20),
(4, 'CZ', '2016-11-04', 150)
;
INSERT INTO payments VALUES
(1, '2016-11-01', 60, 1),
(2, '2016-11-01', 60, 1),
(3, '2016-11-02', 100, 2),
(4, '2016-11-03', 25, 3),
(5, '2016-11-04', 150, 4)
;
INSERT INTO extras VALUES
(1, '2016-11-01', 5, 1),
(2, '2016-11-02', 1, 2),
(3, '2016-11-03', 2, 3),
(4, '2016-11-03', 3, 3),
(5, '2016-11-04', 5, 4)
;
Então nós temos:
- 3 itens em CZ em 1 em PL
- 370 ganhos em CZ e 25 em PL
- Custo de 350 em CZ e 20 em PL
- 11 ganhos extras em CZ e 5 ganhos extras em PL
Agora, quero obter respostas para as seguintes perguntas:
- Quantos itens tivemos no mês passado em todos os países?
- Qual foi o valor total ganho (soma dos pagamentos. montantes) em cada país?
- Qual foi o custo total (soma dos itens.preço) em cada país?
- Qual foi o total de ganhos extras (soma dos valores extras) em cada país?
Com a seguinte consulta ( SQLFiddle ):
SELECT
country AS "group_by",
COUNT(DISTINCT items.id) AS "item_count",
SUM(items.price) AS "cost",
SUM(payments.amount) AS "earned",
SUM(extras.amount) AS "extra_earned"
FROM items
LEFT OUTER JOIN payments ON (items.id = payments.item_id)
LEFT OUTER JOIN extras ON (items.id = extras.item_id)
GROUP BY 1;
Os resultados estão errados:
group_by | item_count | cost | earned | extra_earned
----------+------------+--------+--------+--------------
CZ | 3 | 450.00 | 370.00 | 16.00
PL | 1 | 40.00 | 50.00 | 5.00
Custo e extra_ aprendido para CZ são inválidos - 450 em vez de 350 e 16 em vez de 11. Custo e ganhos para PL também são inválidos - eles são duplicados.
Entendo que, no caso de LEFT OUTER JOIN
, haverá 2 linhas para o item com items.id = 1 (e assim por diante para outras correspondências), mas não sei como criar uma consulta adequada.
Perguntas :
- Como evitar resultados errados na agregação de consultas em várias tabelas?
- Qual é a melhor maneira de calcular a soma sobre valores distintos (items.id nesse caso)?
Versão do PostgreSQL : 9.6.1
Seq Scan
pagamentos, o que significa que a estatística será recalculada em todos os itens. Eu não mencionei isso na pergunta, mas quero filtrar itens também na hora da criação, portanto, precisarei apenas de um subconjunto específico dos dados agregados. Atualizo a pergunta
WHERE
cláusulas ou junções nas subconsultas. Mas verifique também a opção 4 usando LATERAL
.
payments
e items
na subconsulta e adicionar WHERE
a ele? Eu vou ter de referência todas as opções :)
items.created_at
, sim.
OUTER APPLY
e usandoLATERAL
junções.