MYSQL - 按 DESC 顺序按 Id 排序,按 X 分组

Jac*_*dge 0 mysql greatest-n-per-group

在过去的 4 个小时里,我一直专注于这个问题,简而言之,我想按 id 以 DESC 顺序排序该表,按 ads_post_id 分组(基于 id 的 DESC 顺序),LIMIT 为 6行返回。

数据库样本,


id   | ads_post_id
---------------------------------------------------------------------------
22   | 983314845117571
23   | 983314845117571
24   | 983314845117571         
104  | 983314845117571
250  | 983314845117571
253  | 983314845117571 
767  | 983314845117571          
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

我目前的查询,

SELECT * FROM fb_ads GROUP BY ads_post_id ORDER BY id DESC LIMIT 6
Run Code Online (Sandbox Code Playgroud)

然而,这一切的回报是,


id   | ads_post_id
---------------------------------------------------------------------------
22   | 983314845117571   
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

它应该返回,


id    | ads_post_id
---------------------------------------------------------------------------
767   | 983314845117571   
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

很明显,它是按 ASC 顺序分组的,然后按 DESC 顺序按 ID 排序,对吗?

所以这让我在研究中陷入了困境,大多数人似乎将其用作解决方法,但由于性能下降,这并不可取,每次用户进入下一页时都需要调用此查询,

SELECT * FROM 
(
select * from fb_ads order by id desc
) as fb_ads
group by ads_post_id
order by id DESC LIMIT 6
Run Code Online (Sandbox Code Playgroud)

但是,它仍然对我不起作用,这只返回了,

   ---------------------------------------------------------------------------
    id   | ads_post_id
    ---------------------------------------------------------------------------
    22   | 983314845117571   
    ---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

请注意:为了简单起见,这是我的数据库示例,实际上将有数千个ads_post_id,因此据我所知,此时 MYSQL 的MAX()函数将不起作用,因为它只返回一行。

我不是 MYSQL 的专家,但我知道的足够多,我觉得这需要一个超出我专业范围的解决方案。

一些帮助会大有帮助,谢谢。

Kic*_*art 5

由于 MySQL 的一个特性,您误解了 GROUP BY 在 SQL 中的工作方式。在标准 SQL 中,SELECT 语句中的每个非聚合列都必须在 GROUP BY 子句中(对于值 100% 依赖于 GROUP BY 子句中的列的列有一个例外,尽管很少有 SQL 支持这种豁免) .

MySQL 默认不强制执行此操作,但未定义哪些行值用于这些列。虽然您可能会得到想要的,但也可能不会。即使你这样做了,它也有可能在未来发生变化。

排序通常独立于 GROUP BY,但如果您不指定 ORDER 子句,则结果将根据执行 GROUPing 所需的内容进行排序(即,如果它有助于以一种顺序对行进行排序以执行GROUP BY 然后 MySQL 不会费心事后重新排序记录,除非你用 ORDER BY 子句特别告诉它)。

因此,对于您当前的数据,按 ads_post_id 分组,返回的 id 值可能是 22、23、24、104、250、253 或 767。MySQL 选择使用哪一个没有定义。

使用您当前的数据修复,这是微不足道的,因为您可以获得 MAX id:-

SELECT ads_post_id, MAX(id) 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6
Run Code Online (Sandbox Code Playgroud)

MAX 将为每个 GROUPed 值返回 1 行。

正常的问题是人们想要该行的另一列。例如,假设您的示例数据中的每一行也有一个 IP 地址,并且您想要一个等于 ads_post_id 的最高 id 的地址:-

id   | ads_post_id         ip_address
---------------------------------------------------------------------------
22   | 983314845117571     192.168.0.0
23   | 983314845117571     192.168.0.5
24   | 983314845117571     192.168.0.7    
104  | 983314845117571     192.168.0.0
250  | 983314845117571     192.168.0.4
253  | 983314845117571     192.168.0.6
767  | 983314845117571     192.168.0.1     
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您不能只使用 MAX。例如,如果您尝试过:-

SELECT ads_post_id, MAX(id), MAX(ip_address) 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6
Run Code Online (Sandbox Code Playgroud)

你会得到以下数据返回

id   | ads_post_id         ip_address
---------------------------------------------------------------------------
767  | 983314845117571     192.168.0.7     
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

如果您在大多数 SQL 版本中尝试以下操作,您将收到错误消息。在具有默认设置的 MySQL 中,您会得到一个结果,但没有定义返回的 IP 地址(实际上是随机的)。

SELECT ads_post_id, MAX(id), ip_address 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6
Run Code Online (Sandbox Code Playgroud)

对此的解决方案是在子查询中获取每个 ads_post_id 的最大 id,然后将其连接回表以获取其余值:-

SELECT a.ads_post_id,
        a.id,
        a.ip_address
FROM fb_ads a
INNER JOIN
(
    SELECT ads_post_id, MAX(id) AS max_id 
    FROM fb_ads 
    GROUP BY ads_post_id 
) sub0
ON a.ads_post_id = sub0.ads_post_id
AND a.id = sub0.max_id
Run Code Online (Sandbox Code Playgroud)

另一种方法是(ab)使用 GROUP_CONCAT 聚合函数。GROUP_CONCAT 会将所有连接在一起的值带回 1 个字段,每个字段用 , 分隔(默认情况下)。您可以添加 ORDER BY 子句来强制它们连接的顺序。您可以使用 SUBSTRING_INDEX 将所有内容返回到第一个逗号。

这对于简单数据很有用,但对于文本数据或最大为 NULL 的字段会出现问题。

SELECT a.ads_post_id,
        SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY id DESC), ',', 1),
        SUBSTRING_INDEX(GROUP_CONCAT(ip_address ORDER BY id DESC), ',', 1)
FROM fb_ads 
GROUP BY ads_post_id 
Run Code Online (Sandbox Code Playgroud)