Схема :
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
);
Данные :
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)
;
Итак, имеем:
- 3 предмета в CZ в 1 в PL
- 370 заработано в CZ и 25 в PL
- 350 стоит в CZ и 20 в PL
- 11 дополнительных заработанных в CZ и 5 дополнительных заработанных в PL
Теперь я хочу получить ответы на следующие вопросы:
- Сколько предметов у нас было в прошлом месяце в каждой стране?
- Какова была общая заработанная сумма (сумма платежей. Сумм) в каждой стране?
- Какова была общая стоимость (сумма items.price) в каждой стране?
- Каков был общий дополнительный заработок (сумма extras.amount) в каждой стране?
С помощью следующего запроса ( 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;
Результаты неверны:
group_by | item_count | cost | earned | extra_earned
----------+------------+--------+--------+--------------
CZ | 3 | 450.00 | 370.00 | 16.00
PL | 1 | 40.00 | 50.00 | 5.00
Стоимость и extra_earned для CZ недействительны - 450 вместо 350 и 16 вместо 11. Стоимость и заработанные за PL также недействительны - они удваиваются.
Я понимаю, что в случае LEFT OUTER JOIN
будет 2 строки для элемента с items.id = 1 (и так далее для других совпадений), но я не знаю, как построить правильный запрос.
Вопросы :
- Как избежать ошибочных результатов при агрегировании в запросах к нескольким таблицам?
- Каков наилучший способ расчета суммы по отдельным значениям (в этом случае items.id)?
Версия PostgreSQL : 9.6.1
Seq Scan
платежей, что означает, что статистика будет пересчитана по всем статьям. Я не упомянул об этом в этом вопросе, но я хочу также фильтровать элементы по времени создания, поэтому мне понадобится только конкретное подмножество агрегированных данных. Я обновлю вопрос
WHERE
предложения или объединения в подзапросах. Но проверить вариант 4 тоже используя LATERAL
.
payments
и items
в подзапросе и добавить WHERE
к нему? Мне нужно будет сравнить все варианты :)
items.created_at
, да.
OUTER APPLY
и используяLATERAL
соединения вместо этого.