分组顺序

时间:2019-10-17 12:25:19

标签: mysql group-by sql-order-by

目前,我想了解为什么组中的顺序会更改,甚至以为我将其“赋予”了正确的“第一”行。

CREATE TABLE IF NOT EXISTS `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `A` int(11) NOT NULL,
  `B` int(11) NOT NULL,
  `C` int(11) NOT NULL,
  `D` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

INSERT INTO `test` (`id`, `A`, `B`, `C`, `D`) VALUES
(1, 1, 77, 0, 'Vasya'),
(2, 1, 77, 999, 'Masha'),
(6, 1, 77, 999, 'Clone'),
(3, 1, 88, 1, 'Natasha'),
(4, 2, 1, 1, 'Dima'),
(5, 3, 1, 1, 'Katya');

这两个查询给出相同的答案:

SELECT A, B, C, D, id FROM `test` WHERE `A`=1 AND `B`=77 ORDER BY `C` DESC

SELECT DISTINCT A, B, C, D, id FROM `test` WHERE `A`=1 AND `B`=77 ORDER BY `C` DESC

但这两个给出不同的答案:

SELECT * FROM (
    SELECT A, B, C, D, id FROM `test` WHERE `A`=1 AND `B`=77 ORDER BY `C` DESC
) AS t  GROUP BY A, B


SELECT * FROM (
    SELECT DISTINCT A, B, C, D, id FROM `test` WHERE `A`=1 AND `B`=77 ORDER BY `C` DESC
) AS t  GROUP BY A, B

我只想获取最大为'C'的行,它们分别属于一个'A'和一个'B'。但是没有“ DISTINCT”。 我在做什么错了?

PS :我必须添加 A = 1 才能更加具体。在工作项目中没有这种条件,不是查询来选择一行。

2 个答案:

答案 0 :(得分:1)

使用group by时,标准做法是将所有未聚合的列放在group by子句中(或者,如果禁用了MySQL选项ONLY_FULL_GROUP_BY:所有功能上不正常的列取决于group by子句中已有的其他列)。您的查询不符合这条黄金法则:结果是您获得不一致的结果。

在MySQL 8.0中,您可以使用窗口函数解决此问题:

select id, a, b, c, d
from (
    select 
        t.*,
        row_number() over(partition by a, b order by c desc, id) rn
    from test t
) x
where rn = 1

在早期版本中,相关子查询可以完成工作(在您的用例中,它实际上可能比row_number()更有效率):

select t.*
from test t
where id = (
    select id 
    from test t1 
    where t1.a = t.a and t1.b = t.b 
    order by c desc, id 
    limit 1
)

在此 demo on DB Fiddle 中,两个查询均返回:

| id  | A   | B   | C   | D       |
| --- | --- | --- | --- | ------- |
| 2   | 1   | 77  | 999 | Masha   |
| 3   | 1   | 88  | 1   | Natasha |
| 4   | 2   | 1   | 1   | Dima    |
| 5   | 3   | 1   | 1   | Katya   |

答案 1 :(得分:1)

如果您只关心单个A / B组合

select
      t2.*
   from
      ( select t1.A, t1.B, max( t1.id ) highestByABandID
           from test t1
             JOIN
             ( select A, B, max( C ) highestC
                  from test
                  where A = 1 and B = 77
                  group by A, B ) PQ1
                on t1.A = PQ1.A
               AND t1.B = PQ1.B
               AND t1.C = PQ1.highestC
      ) PQ
         JOIN test t2
            on PQ.A = t2.A
           AND PQ.B = t2.B
           AND PQ.highestByABandID = t2.id

如果要使用所有A / B组合,只需删除内部的“ WHERE”子句,即可获得具有各自最高C值的所有A / B组合。与外部的连接将提取与该实例关联的任何记录。

修订的查询。只好添加一个嵌套。对于任何给定的A / B组合,最内层都将获得最高的“ C”值。从那时起,现在仅基于匹配的A / B和最高的“ C”重新加入同一测试表,并获取添加的单个最新ID。现在,每个A / B组合中只有一个具有最高的“ C”值。最后,根据匹配的A / B和最高的“ ID”完成连接。

SQL Fiddle example

相关问题