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

MYSQL - Order By Id In DESC Order, Group By X

在过去的 4 个小时里,我一直专注于这个问题,简直是 shell,我想 订购这个 table按 id 按降序排列,按 ads_post_id 分组(按 id 降序排列),限制为 6 行 returned.

数据库样本,


id   | ads_post_id
---------------------------------------------------------------------------
22   | 983314845117571
23   | 983314845117571
24   | 983314845117571         
104  | 983314845117571
250  | 983314845117571
253  | 983314845117571 
767  | 983314845117571          
---------------------------------------------------------------------------

我当前的查询,

SELECT * FROM fb_ads GROUP BY ads_post_id ORDER BY id DESC LIMIT 6

然而这一切 return 是,


id   | ads_post_id
---------------------------------------------------------------------------
22   | 983314845117571   
---------------------------------------------------------------------------

应该return,


id    | ads_post_id
---------------------------------------------------------------------------
767   | 983314845117571   
---------------------------------------------------------------------------

很明显它已按 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

然而,它仍然对我不起作用,这只是 returned,

   ---------------------------------------------------------------------------
    id   | ads_post_id
    ---------------------------------------------------------------------------
    22   | 983314845117571   
    ---------------------------------------------------------------------------

请注意:这是我的数据库示例,以便于回答,实际上会有数千个 ads_post_id,据我所知这次 MYSQL 的 MAX() 函数将无法工作,因为它只有 return 一行。

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

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

如果你想获得每个 ads_post_id 的最大 ID,开玩笑地通过 ads_post_id 分组获取 max(id) 而不是排序。

SELECT max(id), ads_post_id FROM fb_ads GROUP BY ads_post_id LIMIT 6

你是在要求每组限制,对吗?这在 SQL 中不是一项简单的任务,所以难怪您遇到困难。在 MySQL 中尤其尴尬,因为它们缺少像 ROW_NUMBER().

这样的窗口函数

MySQL 最常见的解决方案是通过递增会话变量并在组将值从一行更改为下一行时重置为 1 来模拟每组的行号。

SELECT id, ads_post_id
FROM (
    SELECT id, ads_post_id,
      @r := IF(@g=ads_post_id, @r+1, 1) AS row_number,
      @g := ads_post_id
    FROM (SELECT @r:=1, @g:=0) as _init, fb_ads
    ORDER BY ads_post_id, id DESC
) AS t
WHERE t.row_number <= 6;

这类问题经常出现,例如我在 2009 年回答的 How to SELECT the newest four items per category?

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

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

排序通常独立于 GROUP BY,但如果您未指定 ORDER 子句,则结果将根据执行 GROUPing 所需的内容进行排序(即,如果它有助于对中的行进行排序一个执行 GROUP BY 然后 MySQL 的命令将不会在之后重新排序记录,除非你用 ORDER BY 子句明确告诉它)。

因此,对于您当前的数据,按 ads_post_id 分组,returned 的 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

MAX 将为每个 GROUPed 值 return 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     
---------------------------------------------------------------------------

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

SELECT ads_post_id, MAX(id), MAX(ip_address) 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6

您将获得以下数据 returned

id   | ads_post_id         ip_address
---------------------------------------------------------------------------
767  | 983314845117571     192.168.0.7     
---------------------------------------------------------------------------

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

SELECT ads_post_id, MAX(id), ip_address 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6

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

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

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

这对于简单数据很有用,但对于文本数据或最多为 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 

@Kickstars 的回答经过深思熟虑并回答了我的问题,但是我使用了略有不同但基于相同概念的解决方案。

我学到的不是得到我想要的结果,ORDER BY必须与GROUP BY分开。

在她的示例中,她使用子查询根据最近的记录对 ads_post_ids 进行分组,然后使用 JOIN 将该数据有效地连接到 [=24= 的其余部分].

这是使用相同的概念,但没有连接,我只是从主 table 查询数据,但使用 WHERE 来包含我的子查询以进行分组。

SELECT   *
FROM     fb_ads
WHERE    (id, ads_post_id) IN (
           SELECT   MAX(id), ads_post_id
           FROM     fb_ads
           GROUP BY ads_post_id)
ORDER BY id DESC LIMIT 6