Schema :
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
);
Dati :
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)
;
Quindi abbiamo:
- 3 articoli in CZ in 1 in PL
- 370 guadagnati in CZ e 25 in PL
- 350 costo in CZ e 20 in PL
- 11 extra guadagnati in CZ e 5 extra guadagnati in PL
Ora voglio ottenere risposte per le seguenti domande:
- Quanti articoli abbiamo avuto il mese scorso in ogni paese?
- Qual è stato l'importo totale guadagnato (somma dei pagamenti. Importi) in ogni paese?
- Qual è stato il costo totale (somma di articoli.prezzo) in ogni paese?
- Qual è stato il totale delle entrate extra (somma degli extra) in ogni paese?
Con la seguente query ( 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;
I risultati sono sbagliati:
group_by | item_count | cost | earned | extra_earned
----------+------------+--------+--------+--------------
CZ | 3 | 450.00 | 370.00 | 16.00
PL | 1 | 40.00 | 50.00 | 5.00
I costi e gli extra appresi per CZ non sono validi - 450 invece di 350 e 16 invece di 11. Anche i costi e i guadagni per PL non sono validi - sono raddoppiati.
Capisco che in caso LEFT OUTER JOIN
ci siano 2 righe per l'articolo con items.id = 1 (e così via per altre corrispondenze), ma non so come creare una query corretta.
Domande :
- Come evitare risultati errati nell'aggregazione nelle query su più tabelle?
- Qual è il modo migliore per calcolare la somma su valori distinti (items.id in quel caso)?
Versione PostgreSQL : 9.6.1
Seq Scan
sui pagamenti, il che significa che la statistica verrà ricalcolata su tutti gli articoli. Non ho menzionato questo nella domanda, ma voglio filtrare gli elementi anche al momento della creazione, quindi avrò bisogno solo di un sottoinsieme specifico dei dati aggregati. Aggiornerò la domanda
WHERE
clausole o join nelle sottoquery. Ma controlla anche l'opzione 4, usando LATERAL
.
payments
e items
in subquery e aggiungerlo WHERE
? Dovrò confrontare tutte le opzioni :)
items.created_at
, sì.
OUTER APPLY
e usandoLATERAL
invece i join.