Заказать в группе по - PullRequest
       55

Заказать в группе по

1 голос
/ 17 октября 2019

В настоящее время я хочу понять, почему порядок в группе меняется, даже подумав, что я «даю» ему правильную «первую» строку.

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». Но без «ОТЛИЧИЯ». Что я делаю не так?

PS : мне пришлось добавить A = 1 , чтобы быть более точным. В рабочем проекте такого условия нет, это , а не запрос на выбор одной строки.

Ответы [ 2 ]

1 голос
/ 17 октября 2019

Если вас интересует только одна комбинация 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, чтобы получить все комбинации A / B с ихсоответствующее наибольшее значение C. Соединение с внешним извлечет любую запись, связанную с этим одним экземпляром.

Исправленный запрос. Пришлось добавить еще одну вложенность. Самое внутреннее захватывает самое высокое значение «C» для любой данной комбинации A / B. Исходя из этого, теперь снова присоединитесь к той же тестовой таблице, основываясь только на тех, которые соответствуют A / B и наибольшему «C», и возьмите последний добавленный идентификатор. Теперь у вас будет только 1 на комбинацию A / B, которая имеет наибольшее значение «C». Наконец, завершите объединение на основе соответствующего A / B и наибольшего «ID».

Пример SQL Fiddle

1 голос
/ 17 октября 2019

При использовании 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
)

В этой демонстрации на 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   |
...