获取按用户分组的总成本
Posted
技术标签:
【中文标题】获取按用户分组的总成本【英文标题】:Getting Total Cost Grouped by User 【发布时间】:2015-04-11 22:07:08 【问题描述】:我已经为这个查询苦苦挣扎了两天了。我有一个 user 表,其中一些值与 order 表有关系(用户可以有多个订单)。此表与 order_item 有关系(order 可以有多个 orderItems)。 Order_Item 与 invoice 有关系(order_item 可以有多个发票。
分店和店铺与用户是一对一的关系。
以下是所有表格中最重要的值:
user:
-userId (int)
order
-orderId (int)
-userId (int)
-inserted (date)
order_item
-orderItemId (int)
-orderId (int)
invoice
-invoiceId (int)
-orderItemId (int)
-cost (double)
这里的外键是不言自明的。用户->订单->订单项->发票。 我需要的是一个查询,其中结果中的每一行代表一个用户,两列代表 2014 年和 2015 年的总销售额(成本总和)。
所以它要做的就是在一行中显示每个用户,其中包含用户表中的一些信息(公司名称、电子邮件等)和两列,根据订单显示 2014 年的总成本和 2015 年的总成本.插入的日期值。
一个例子是:
姓名:|电子邮件 | 2014 成本 | 2015 年成本
谷歌 | info@google.com | 50.000 欧元 | 45.000 欧元
现在我已经得到了第一个总和的结果(显示所有用户,无论费用如何),只有当我第二次加入时(计算 2015 年的费用),我之前的总费用才完全得到搞砸了。
我在连接中尝试了一些选择查询,但我无法让任何查询工作。我不是一个完整的 SQL 初学者,但这对我来说太复杂了,无法弄清楚这个确切的时刻。
这是我用来获取 2014 年结果的查询(一旦我为 2015 年添加第二个联接,它就会搞砸):
SELECT t.userId, SUM(i.cost),
t.companyName, t.email,
t.website, t.tel, t.priority,
b.name AS Branch, s.name AS `Shop Name`
FROM `user` AS t
LEFT JOIN branch AS b ON b.branchId = t.branchId
LEFT JOIN shop AS s ON s.shopId = t.shopId
LEFT JOIN `order` AS o ON (o.userId = t.userId AND YEAR(o.inserted) = 2014)
LEFT JOIN order_item AS oi ON oi.orderId = o.orderId
LEFT JOIN invoice AS i ON i.orderItemId = oi.orderItemId
GROUP BY t.userId
我真的希望有人可以帮助我解决这个问题。 (我在 Navicat 8 中使用 mysql/innoDB)。
【问题讨论】:
branch
和 shop
表是否会为每个 userId
生成多个记录,或者这是一对一的关系?这对GROUP BY
的申请很重要。
分店和店铺引用与用户是一对一的关系。感谢您指出这一点,并将其添加到问题中。
仅供参考,这种将 2014 年和 2015 年分开列的业务在行话中称为“枢轴”。
【参考方案1】:
最终,这是您尝试制作的一种数据透视表。您可以将条件直接放在 SUM()
聚合中,而不是在连接的 ON
子句中连接和测试年份条件,例如:
-- If the year matches, add the cost value into the sum
-- Otherwise, add zero
SUM(CASE WHEN YEAR(o.inserted) = 2014 THEN i.cost ELSE 0 END) AS `2014 Cost`
这消除了对那些额外连接的需要。应用GROUP BY
时,它应该包括每个组可能不同的所有列。 MySQL 允许您从GROUP BY
中省略SELECT
中的列,其中大多数其他RDBMS 会导致查询编译错误。
SELECT
t.userId,
-- Apply the aggregate SUM() conditionally for each year
SUM(CASE WHEN YEAR(o.inserted) = 2014 THEN i.cost ELSE 0 END) AS `2014 Cost`
SUM(CASE WHEN YEAR(o.inserted) = 2015 THEN i.cost ELSE 0 END) AS `2015 Cost`
t.companyName,
t.email,
t.website,
t.tel,
t.priority,
b.name AS Branch,
s.name AS `Shop Name`
FROM
`user` AS t
LEFT JOIN branch AS b ON b.branchId = t.branchId
LEFT JOIN shop AS s ON s.shopId = t.shopId
LEFT JOIN `order` AS o ON (o.userId = t.userId)
LEFT JOIN order_item AS oi ON oi.orderId = o.orderId
LEFT JOIN invoice AS i ON i.orderItemId = oi.orderItemId
GROUP BY
t.userId,
-- Adding remaining SELECT fields
-- though MySQL will allow these to be omitted
-- without breaking this particular query
t.companyName,
t.email,
t.website,
t.tel,
t.priority,
Branch,
`Shop Name`
【讨论】:
你也可以加入到 order table 两次,加上一年的附加限定符——这可能比某些系统上的情况更快,具体取决于索引等。 谢谢,这就是我要找的。感谢您的提示和答案,真的帮助了我! @Hogan:对于这类问题,双重联接是一种不好的做法,添加一个简单的日期过滤器应该会更好:“o.inserted between >”效果更好。 @PeterKrassoi - 我会小心使用像坏习惯这样的术语 - 这种技术显然不是“坏习惯”。它可能不是最快的解决方案,但在许多情况下,它只是一种标准的 SQL 技术。这在很大程度上取决于数据和数据库结构,哪种技术最好。了解所有技术并应用最好的技术是一种很好的做法。以上是关于获取按用户分组的总成本的主要内容,如果未能解决你的问题,请参考以下文章