【发布时间】:2021-12-27 15:49:56
【问题描述】:
鉴于以下情况:
我有一个包含 5 个表的数据库:
- 货币(iso_number,iso_code),
- 产品(ID、名称、当前价格),
- 销售(id、time_of_sale、currency_items_sold_in),
- sale_lines(id、sale_id、product_id、price_paid、数量),
- cash_transactions(id、sale_id、receed_currency_id、converted_currency_id、receed_amount、converted_amount)
该设置允许存储客户最初提供的货币种类,内部兑换的货币以及原始金额和兑换(转换)的金额。
我希望能够找到符合特定条件(时间段、卖家、商店)等的所有销售((为简单起见,省略)。
对于所有这些销售,我将加入相关数据,即 sale_lines 和 cash_transactions。现在 sale_lines 上的货币始终与相关销售上的货币相匹配。 但是,对于 cash_transactions,received_amount/received_currency 可能与销售时的货币不同。尽管converted_currency/converted_amount 存储在cash_transaction 行中,但它应该跟随销售。
当我尝试对某些字段执行 SUM 时出现问题,当您开始加入一对多关系然后执行像 SUM 这样的聚合函数时,即使您在幕后指定了正确的 GROUP BY,SQL Server 仍然会对如果我们不使用 GROUP BY,则显示数据所需的重复行。
这里也描述了这个问题: https://wikido.isoftdata.com/index.php/The_GROUPing_pitfall
按照上面文章中的解决方案,在我的情况下,我应该将每次销售的汇总结果加入到外部查询中。
但是当 sale_lines 货币与销售匹配时,我该怎么办,但 cash_transactions 货币可能与销售不同。
我尝试创建以下 SQL Fiddle,它插入一些测试数据并突出显示问题:http://sqlfiddle.com/#!17/54a7b/15
在小提琴中,我创建了 2 个销售,其中的商品以丹麦克朗(208)和 752(瑞典克朗)出售。 第一次销售有2条销售线,2笔现金交易,第一笔交易直接是DKK => DKK,第二笔交易是SEK => DKK。
第二次销售也有2条销售线,2笔现金交易,第一笔交易是NOK => DKK,第二笔交易直接是DKK => DKK。
在小提琴的最后一个查询中,可以观察到 total_received_amount 是伪造的,因为它是 DKK、SEK 和 NOK 的混合,并没有提供太多价值。
我想要关于如何正确获取数据的建议,我不在乎我是否必须在服务器端 (PHP) 上执行额外的“逻辑”以便对一些数据进行重复数据删除,只要总和是正确的。
非常感谢任何建议。
来自 FIDDLE 的 DDL
CREATE TABLE currency (
iso_number CHARACTER VARYING(3) PRIMARY KEY,
iso_code CHARACTER VARYING(3)
);
INSERT INTO currency(iso_number, iso_code) VALUES ('208','DKK'), ('752','SEK'), ('572','NOK');
CREATE TABLE product (
id SERIAL PRIMARY KEY,
name CHARACTER VARYING(12),
current_price INTEGER
);
INSERT INTO product(id,name,current_price) VALUES (1,'icecream',200), (2,'sunglasses',300);
CREATE TABLE sale (
id SERIAL PRIMARY KEY,
time_of_sale TIMESTAMP,
currency_items_sold_in CHARACTER VARYING(3)
);
INSERT INTO sale(id, time_of_sale, currency_items_sold_in)
VALUES
(1, CURRENT_TIMESTAMP, '208'),
(2, CURRENT_TIMESTAMP, '752')
;
CREATE TABLE sale_lines (
id SERIAL PRIMARY KEY,
sale_id INTEGER,
product_id INTEGER,
price_paid INTEGER,
quantity FLOAT
);
INSERT INTO sale_lines(id, sale_id, product_id, price_paid, quantity)
VALUES
(1, 1, 1, 200, 1.0),
(2, 1, 2, 300, 1.0),
(3, 2, 1, 100, 1.0),
(4, 2, 1, 100, 1.0)
;
CREATE TABLE cash_transactions (
id SERIAL PRIMARY KEY,
sale_id INTEGER,
received_currency_id CHARACTER VARYING(3),
converted_currency_id CHARACTER VARYING(3),
received_amount INTEGER,
converted_amount INTEGER
);
INSERT INTO cash_transactions(id, sale_id, received_currency_id, converted_currency_id, received_amount, converted_amount)
VALUES
(1, 1, '208', '208', 200, 200),
(2, 1, '752', '208', 400, 300),
(3, 2, '572', '208', 150, 100),
(4, 2, '208', '208', 100, 100)
;
来自 FIDDLE 的查询
--SELECT * FROM currency;
--SELECT * FROM product;
--SELECT * FROM sale;
--SELECT * FROM sale_lines;
--SELECT * FROM cash_transactions;
--- Showing the sales with duplicated lines to
--- fit joined data for OneToMany SaleLines, and OneToMany cash transactions.
SELECT *
FROM sale s
LEFT JOIN sale_lines sl ON sl.sale_id = s.id
LEFT JOIN cash_transactions ct ON ct.sale_id = s.id;
--- Grouping the data by important identifier "currency_items_sold_in".
--- The SUM of sl.price_paid is wrong as it SUMS the duplicated lines as well.
SELECT
s.currency_items_sold_in,
SUM(sl.price_paid) as "price_paid"
FROM sale s
LEFT JOIN sale_lines sl ON sl.sale_id = s.id
LEFT JOIN cash_transactions ct ON ct.sale_id = s.id
GROUP BY s.currency_items_sold_in;
--- To solve this the SUM can be joined via the "Monkey-Poop" method.
--- Here the problem arises, the SUMS for cash_transaction.received_amount and cash_transaction.converted_amount cannot be relied upon
--- As those fields themselves rely on cash_transaction.received_currency_id and cash_transaction.converted_currency_id
SELECT
s.currency_items_sold_in,
SUM(sale_line_aggregates.price_paid) as "total_price_paid",
SUM(cash_transaction_aggregates.converted_amount) as "total_converted_amount",
SUM(cash_transaction_aggregates.received_amount) as "total_received_amount"
FROM sale s
LEFT JOIN (
SELECT
sale_id,
SUM(price_paid) AS price_paid
FROM sale_lines
GROUP BY sale_id
) AS sale_line_aggregates ON sale_line_aggregates.sale_id = s.id
LEFT JOIN (
SELECT
sale_id,
SUM(converted_amount) as converted_amount,
SUM(received_amount) as received_amount
FROM cash_transactions
GROUP BY sale_id
) AS cash_transaction_aggregates ON cash_transaction_aggregates.sale_id = s.id
GROUP BY s.currency_items_sold_in;
【问题讨论】:
标签: php sql postgresql aggregate aggregate-functions