sql - 如何选择每个类别最新的四个项目?

标签 sql mysql greatest-n-per-group

我有一个项目数据库。每个项目都使用类别表中的类别 ID 进行分类。我正在尝试创建一个列出每个类别的页面,并且在每个类别下我想显示该类别中的 4 个最新项目。

例如:

宠物用品

img1
img2
img3
img4

宠物食品

img1
img2
img3
img4

我知道我可以通过查询每个类别的数据库来轻松解决这个问题,如下所示:

SELECT id FROM category

然后迭代该数据并查询每个类别的数据库以获取最新的项目:

SELECT image FROM item where category_id = :category_id 
ORDER BY date_listed DESC LIMIT 4

我想要弄清楚的是我是否可以只使用 1 个查询并获取所有数据。我有 33 个类别,因此我认为这可能有助于减少对数据库的调用次数。

有人知道这是否可行吗?或者,如果 33 个电话没什么大不了的,我应该用简单的方法来做。

最佳答案

这是最大的每组问题,也是一个非常常见的 SQL 问题。

这是我如何使用外连接解决这个问题:

SELECT i1.*
FROM item i1
LEFT OUTER JOIN item i2
  ON (i1.category_id = i2.category_id AND i1.item_id < i2.item_id)
GROUP BY i1.item_id
HAVING COUNT(*) < 4
ORDER BY category_id, date_listed;

我假设 item 表的主键是 item_id,并且它是一个单调递增的伪键。也就是说,item_id 中的较大值对应于 item 中较新的行。

其工作原理如下:对于每个项目,都有一些更新的其他项目。例如,有三个项目比第四个最新项目更新。比最新项目更新的项目为零。因此,我们想要将每个项目 (i1) 与较新且与 i1 具有相同类别的项目集 (i2) 进行比较。如果这些较新项目的数量少于四个,i1 就是我们包含的项目之一。否则,请勿包含它。

此解决方案的优点在于,无论您有多少类别,它都可以工作,并且如果您更改类别,它仍然可以继续工作。即使某些类别中的项目数量少于四个,它也适用。

<小时/>

另一个可行的解决方案,但依赖于 MySQL 用户变量功能:

SELECT *
FROM (
    SELECT i.*, @r := IF(@g = category_id, @r+1, 1) AS rownum, @g := category_id
    FROM (@g:=null, @r:=0) AS _init
    CROSS JOIN item i
    ORDER BY i.category_id, i.date_listed
) AS t
WHERE t.rownum <= 3;
<小时/>

MySQL 8.0.3 引入了对 SQL 标准窗口函数的支持。现在我们可以像其他 RDBMS 一样解决此类问题:

WITH numbered_item AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY item_id) AS rownum
  FROM item
)
SELECT * FROM numbered_item WHERE rownum <= 4;

关于sql - 如何选择每个类别最新的四个项目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40362651/

相关文章:

php - 无法在 PHP 文件中使用 Bootstrap

mysql - 如何找到从事完全相同项目的 worker 数量

SQL - 返回编号最高的标题

mysql - 在SQL中找到一个变量的最小值和其他变量的匹配值

SQL 服务器 : need to add a primary key

mysql - 计算列的 View 数如何影响性能?

sql - 在 Spring Boot 中创建原生 SQL 查询而不创建实体类

MySQL 查询返回当前日期的 -7 到 +7?

php - 如何仅在经过 1 小时后才更新 SQL 数据

sql-server - TSQL - 选择计数较高的行,当计数相同时,选择 id 值较高的行