获取按用户分组的总成本

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)。

【问题讨论】:

branchshop 表是否会为每个 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 技术。这在很大程度上取决于数据和数据库结构,哪种技术最好。了解所有技术并应用最好的技术是一种很好的做法。

以上是关于获取按用户分组的总成本的主要内容,如果未能解决你的问题,请参考以下文章

如何获得分组列的前 10 名?

获取按日期分组的最近 n 天的活跃用户数量

Mongodb,按日期差异分组并获取小时

在 DB2/400 中获取仅按用户分组的最新结果

JOOQ:如何获取按字段分组的记录?

如何按一列的最大值获取SQL行,按另一列分组