MYSQL - 按DESC顺序排序,按X排序

时间:2016-10-24 05:11:02

标签: mysql greatest-n-per-group

在过去的4个小时里,我一直专注于这个问题,在坚果壳中,我想 按DESC顺序按ID排序此表,按ads_post_id分组(在DESC中)基于id的订单,返回了6行的LIMIT。

数据库样本,

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顺序分组,然后按照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

然而,它仍然没有为我工作,这只是返回,

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

请注意:这是我的数据库样本,为了简单回答,实际上会有成千上万的{{​​1}},所以据我所知MYSQL&#39 ; ads_post_id函数不起作用,因为它只返回一行。

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

一些帮助会有很长的路要走,谢谢。

4 个答案:

答案 0 :(得分:4)

由于MySQL的一项功能,您误解了GROUP BY如何在SQL中工作。在标准SQL中,SELECT语句中的每个非聚合列必须位于GROUP BY子句中(对于其值100%依赖于GROUP BY子句中已有的列的列存在例外,尽管很少有SQL支持此豁免)

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

排序通常独立于GROUP BY,但如果您没有指定ORDER子句,那么结果将根据执行GROUPing所需的顺序排序(即,如果它有助于按顺序排序行要做GROUP BY,那么除非你用ORDER BY子句明确告诉它,否则MySQL不会费心对记录进行重新排序。)

因此,根据您当前的数据,按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个字段,每个字段用a分隔(默认情况下)。您可以添加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 

答案 1 :(得分: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?

答案 2 :(得分:0)

如果您想获得每个ads_post_id jest的最大ID,请通过ads_post_id获取max(id),而不是订购。

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

答案 3 :(得分:0)

@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