【问题标题】:MYSQL - Order By Id In DESC Order, Group By XMYSQL - 按 DESC 顺序按 Id 排序,按 X 分组
【发布时间】:2016-10-24 19:45:44
【问题描述】:

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

数据库样本,


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

然而这一切的回报是,


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

它应该返回,


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

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

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

SELECT * FROM 
(
select * from fb_ads order by id desc
) as fb_ads
group by ads_post_id
order by id DESC LIMIT 6

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

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

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

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

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

【问题讨论】:

  • 以后不要再花4个小时在上面问了
  • 期望的结果是什么样的? LIMIT 6 是干什么用的?这里只有一个 ads_post_id,所以关于 GROUP BY 的所有内容似乎都无关紧要。

标签: mysql greatest-n-per-group


【解决方案1】:

由于 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

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     
---------------------------------------------------------------------------

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

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

你会得到以下数据返回

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

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

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

解决方案是获取子查询中每个 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

另一种方法是(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 

【讨论】:

  • 感谢您的精彩回复,我已将其标记为您应得的答案。我还根据您描述的相同概念,使用我使用的确切解决方案添加了一个答案,但没有使用连接。再次感谢。
【解决方案2】:

您要求每个组有限制,对吗?这在 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?

【讨论】:

【解决方案3】:

如果您想为每个 ads_post_id 获取最大 id,请按 ads_post_id 分组而不是 ordering 获取 max(id)。

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

【讨论】:

  • 这似乎没有得到我想要的结果,它不是按最近的 id 排序的,这是必须的。它必须按ID按DESC顺序排序,但按ads_post_id分组(在DESC中基于DESC排序ID)
【解决方案4】:

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

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

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

这是使用相同的概念但没有连接,我只是从我的主表中查询数据,但使用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

【讨论】:

  • 嗨。这将起作用,但 MySQL 使用 IN 可能会非常低效。这是否是一个问题将取决于子查询返回的数据量。 PS - 这是
  • 抱歉!我已经编辑了我的答案。我一定会记住这一点,现在这不是什么大问题,而且我已经将它合并到我的 PHP 中,并且已经使用了很多次,所以我暂时搁置它。再次感谢。
猜你喜欢
  • 2011-06-08
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-14
  • 2021-10-07
  • 2021-12-13
相关资源
最近更新 更多