我正在研究一个简单的MySQL订购系统,我遇到了这个障碍,我希望一些SQL天才可以帮助我。
我有一个表,用于订单,付款(带有对Order表的外键引用)和OrderItems(也有一个对Order表的外键引用),我想要做的是获得总计未完成的使用单个查询的订单余额(总计和付费)。我最初的想法是做一些像这样简单的事情:
SELECT Order.*, SUM(OrderItem.Amount) AS Total, SUM(Payment.Amount) AS Paid
FROM Order
JOIN OrderItem ON OrderItem.OrderId = Order.OrderId
JOIN Payment ON Payment.OrderId = Order.OrderId
GROUP BY Order.OrderId
但是,如果有多个付款或多个OrderItems,它会分别混淆Total或Paid(例如,一个OrderItem记录,金额为100,另外两个付款记录将产生总计200)。
为了克服这个问题,我可以通过以下方式使用一些子查询:
SELECT Order.OrderId, OrderItemGrouped.Total, PaymentGrouped.Paid
FROM Order
JOIN (
SELECT OrderItem.OrderId, SUM(OrderItem.Amount) AS Total
FROM OrderItem
GROUP BY OrderItem.OrderId
) OrderItemGrouped ON OrderItemGrouped.OrderId = Order.OrderId
JOIN (
SELECT Payment.OrderId, SUM(Payment.Amount) AS Paid
FROM Payment
GROUP BY Payment.OrderId
) PaymentGrouped ON PaymentGrouped.OrderId = Order.OrderId
你可以想象(并且这个查询会显示EXPLAIN
),这不是一个最佳查询,所以,我想知道,有没有办法用{{1转换这两个子查询}}语句到GROUP BY
s?
答案 0 :(得分:3)
使用正确的索引可能会更快:
select o.OrderId,
(select sum(oi.Amount)
from OrderItem oi
where oi.OrderId = o.OrderId
) as Total,
(select sum(p.Amount)
from Payment p
where oi.OrderId = o.OrderId
) as Paid
from Order o;
正确的索引是OrderItem(OrderId, Amount)
和Payment(OrderId, Amount)
。
我不喜欢用这种方式编写聚合查询,但它有时可以帮助MySQL中的性能。
答案 1 :(得分:1)
有些答案已经建议使用相关的子查询,但是没有真正解释为什么。 MySQL没有实现相关的子查询,但它将实现派生表。也就是说现在使用查询的简化版本:
SELECT Order.OrderId, OrderItemGrouped.Total
FROM Order
JOIN (
SELECT OrderItem.OrderId, SUM(OrderItem.Amount) AS Total
FROM OrderItem
GROUP BY OrderItem.OrderId
) OrderItemGrouped ON OrderItemGrouped.OrderId = Order.OrderId;
在执行开始时,MySQL会将子查询的结果放入临时表中,并在OrderId上散列此表以便更快地查找,而如果运行:
SELECT Order.OrderId,
( SELECT SUM(OrderItem.Amount)
FROM OrderItem
WHERE OrderItem.OrderId = OrderId
) AS Total
FROM Order;
对于Order中的每一行,子查询将被执行一次。如果添加WHERE Order.OrderId = 1
之类的东西,聚合整个OrderItem表显然效率不高,将结果散列为仅查找一个值,但如果要返回所有订单,则创建哈希表的初始成本将使为了它自己,它不必为Order表中的每一行执行子查询。
如果您选择了很多行并认为实现有益,您可以按如下方式简化您的JOIN查询:
SELECT Order.OrderId, SUM(OrderItem.Amount) AS Total, PaymentGrouped.Paid
FROM Order
INNER JOIN OrderItem
ON OrderItem.OrderID = Order.OrderID
INNER JOIN
( SELECT Payment.OrderId, SUM(Payment.Amount) AS Paid
FROM Payment
GROUP BY Payment.OrderId
) PaymentGrouped
ON PaymentGrouped.OrderId = Order.OrderId;
GROUP BY Order.OrderId, PaymentGrouped.Paid;
然后你只有一个派生表。
答案 2 :(得分:0)
这样的事情:
SELECT Order.OrderId, (
SELECT SUM(OrderItem.Amount)
FROM OrderItem as OrderItemGrouped
where
OrderItemGrouped.OrderId = Order.OrderId
), AS Total,
(
SELECT SUM(Payment.Amount)
FROM Payment as PaymentGrouped
where
PaymentGrouped.OrderId = Order.OrderId
) as Paid
FROM Order
PS:你再次获胜@Gordon xD
答案 3 :(得分:0)
Select o.orderid, i.total, s.paid
From orders o
Left join (select orderid, sum(amount)
From orderitem) i
On i.orderid = o.orderid
Ieft join (select orderid, sum(amount)
From payments) s
On s.orderid = o.orderid