mysql分组取每组前几条记录(排名) 附group by与order by的研究


在MySQL中,当你需要按照某个字段进行分组,并且从每个分组中选取前几条记录时,你可以使用窗口函数(Window Functions)或者通过子查询和JOIN操作来实现。这里我将给出两种方法的示例,一种使用窗口函数(MySQL 8.0及以上版本支持),另一种使用变量和子查询(适用于更旧的MySQL版本)。

### 方法一:使用窗口函数(推荐,MySQL 8.0+)

假设你有一个表`sales`,其中包含字段`year`(年份)、`product_id`(产品ID)和`amount`(销售额),你想要对每个`year`分组,并从每个分组中选取`amount`最高的前2条记录。


WITH RankedSales AS (
  SELECT
    year,
    product_id,
    amount,
    ROW_NUMBER() OVER(PARTITION BY year ORDER BY amount DESC) AS rn
  FROM
    sales
)
SELECT
  year,
  product_id,
  amount
FROM
  RankedSales
WHERE
  rn <= 2;

在这个查询中,`ROW_NUMBER()`窗口函数为每个`year`分组内的记录按`amount`降序排列分配了一个唯一的序号(`rn`)。然后,我们从这个结果集中选择序号小于或等于2的记录。

### 方法二:使用变量和子查询(MySQL 8.0以下)

如果你使用的是MySQL 8.0以下的版本,不支持窗口函数,你可以使用变量来模拟这个功能。


SET @rank = 0, @year_prev = NULL;

SELECT
  year,
  product_id,
  amount
FROM (
  SELECT
    year,
    product_id,
    amount,
    @rank := IF(@year_prev = year, @rank + 1, 1) AS rn,
    @year_prev := year
  FROM
    sales
    ORDER BY year, amount DESC
) AS ranked_sales
WHERE
  rn <= 2;

注意:这个方法在某些情况下可能不是完全稳定的,特别是当`year`字段中的值有重复且需要精确控制排序时。此外,它依赖于MySQL的变量和排序的顺序,这可能在不同的MySQL版本或配置中有所不同。

### 总结

- 对于MySQL 8.0及更高版本,推荐使用窗口函数,因为它们提供了更清晰、更易于理解的查询语法。

- 对于旧版本的MySQL,你可以使用变量和子查询来模拟分组取前几名的功能,但请注意其潜在的限制和不稳定性。

这两种方法都涉及到了`GROUP BY`和`ORDER BY`的使用,但在这个特定的查询中,`GROUP BY`实际上并没有直接用于获取每个分组的前几条记录,而是`ORDER BY`在子查询或窗口函数中用于确定记录的顺序,然后通过`ROW_NUMBER()`或变量分配排名。