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
);
Datos :
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)
;
Entonces tenemos:
- 3 artículos en CZ en 1 en PL
- 370 ganados en CZ y 25 en PL
- 350 costo en CZ y 20 en PL
- 11 extra ganados en CZ y 5 extra ganados en PL
Ahora quiero obtener respuestas a las siguientes preguntas:
- ¿Cuántos artículos tuvimos el mes pasado en cada país?
- ¿Cuál fue el monto total ganado (suma de pagos. Montos) en cada país?
- ¿Cuál fue el costo total (suma de artículos.precio) en cada país?
- ¿Cuál fue el total de ganancias adicionales (suma de cantidades adicionales) en cada país?
Con la siguiente 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;
Los resultados son incorrectos:
group_by | item_count | cost | earned | extra_earned
----------+------------+--------+--------+--------------
CZ | 3 | 450.00 | 370.00 | 16.00
PL | 1 | 40.00 | 50.00 | 5.00
El costo y extra_earned para CZ no son válidos: 450 en lugar de 350 y 16 en lugar de 11. El costo y el ganado para PL también no son válidos: se duplican.
Entiendo que en caso de LEFT OUTER JOIN
que haya 2 filas para el elemento con items.id = 1 (y así sucesivamente para otras coincidencias), pero no sé cómo construir una consulta adecuada.
Preguntas :
- ¿Cómo evitar resultados incorrectos en la agregación de consultas en varias tablas?
- ¿Cuál es la mejor manera de calcular la suma sobre valores distintos (items.id en ese caso)?
Versión PostgreSQL : 9.6.1
Seq Scan
pagos, lo que significa que la estadística se volverá a calcular en todos los artículos. No mencioné esto en la pregunta, pero también quiero filtrar los elementos por tiempo de creación, por lo que solo necesitaré un subconjunto específico de los datos agregados. Actualizaré la pregunta
WHERE
cláusulas o uniones en las subconsultas. Pero marque la opción 4, también, usando LATERAL
.
payments
y items
en subconsulta y agregarle WHERE
? Tendré que comparar todas las opciones :)
items.created_at
, sí.
OUTER APPLY
y utilizandoLATERAL
combinaciones en su lugar.