首页 > 解决方案 > SQL:获取组第一行的正确方法

问题描述

我正在将一个站点迁移到 Google 的 Cloud SQL 服务,该服务具有一个奇怪的默认值ONLY_FULL_GROUP_BY,这意味着我使用的一个常见模式突然崩溃了。

考虑以下:

SELECT `p`.`id`, `p`.`name`, `s`.`name` AS `latest_purchase`, `s`.`price` AS `latest_purchase_price`
FROM `Person` p
JOIN `Sales` s ON `s`.`person` = `p`.`ID`
GROUP BY `p`.`id`
ORDER BY `p`.`time` DESC

我想要的是得到一个结果列表,其中每一行都是一个独特的人,列表示他们最近购买的名称和价格。

我在 MySQL 中习惯的部分分组行为对此非常有用,因为它仅根据人员 ID 分组,并且由于我的结果是有序的,每个组的第一行是我想要的,所以我得到的结果是我预计。

但是这在 SQL 模式下是不允许的,ONLY_FULL_GROUP_BY它要求所有选定的项目要么在GROUP BY子句中,要么使用聚合函数来选择单个结果。

在上面的示例中,这些都不起作用,因为将所有内容添加到GROUP BY每个人都会得到多个结果的方法中,而使用聚合函数可能会给出不准确的结果,因为销售价格不一定是组中的最高或最低(我可能以销售名称和价格告终,但两者都不是最新的)。

幸运的是 SQL 模式是 Google SQL 允许用户更改的设置之一,所以我暂时这样做了(编辑实例并设置标志)。

但是,如果我将来要使用另一个无法按我希望进行分组的系统,那么当不允许部分分组时,这样做的“正确”方法是什么?

我意识到在 StackOverflow 上已经有一些类似的问题,但是我发现没有一个能完全捕捉到我的问题(因为它们涉及可以使用聚合函数的更简单的示例)。

标签: mysqlsql

解决方案


你想要的是过滤而不是聚合

SELECT `p`.`id`, `p`.`name`, `s`.`name` AS `latest_purchase`, `s`.`price` AS `latest_purchase_price`
FROM `Person` p JOIN
     `Sales` s
     ON `s`.`person` = `p`.`ID`
WHERE s.time = (SELECT MAX(s2.time) FROM sales s2 WHERE s2.person = s.person)
ORDER BY `p`.`time` DESC

推荐阅读