MySQL - контролирует, какая строка возвращается группой - PullRequest
56 голосов
/ 11 февраля 2009

У меня есть таблица базы данных, подобная этой:

id    version_id    field1    field2
1     1             texta      text1
1     2             textb      text2
2     1             textc      text3
2     2             textd      text4
2     3             texte      text5

Если вы не работали, он содержит несколько версий строки, а затем некоторые текстовые данные.

Я хочу запросить его и вернуть версию с наибольшим номером для каждого идентификатора. (поэтому вторые и последние строки только в приведенном выше).

Я пытался использовать группу, упорядочивая по version_id DESC - но, похоже, она упорядочена после группировки, поэтому это не работает.

У кого-нибудь есть идеи? Я не могу поверить, что это не может быть сделано!

UPDATE:

Придумайте это, который работает, но использует подзапрос:

SELECT *
FROM (SELECT * FROM table ORDER BY version_id DESC) t1
GROUP BY t1.id

Ответы [ 9 ]

49 голосов
/ 11 февраля 2009

Это называется выбором группового максимума столбца. Вот несколько разных подходов для mysql.

Вот как бы я это сделал:

SELECT *
FROM (SELECT id, max(version_id) as version_id FROM table GROUP BY id) t1
INNER JOIN table t2 on t2.id=t1.id and t1.version_id=t2.version_id

Это будет относительно эффективно, хотя mysql создаст временную таблицу в памяти для подзапроса. Я предполагаю, что у вас уже есть индекс (id, version_id) для этой таблицы.

Недостаток SQL в том, что вам более или менее необходимо использовать подзапрос для такого типа проблемы ( полусоединения - другой пример).

Подзапросы не очень хорошо оптимизированы в mysql, но некоррелированные подзапросы не так плохи, если они не настолько огромны, что будут записываться на диск, а не в память. Учитывая, что в этом запросе есть только два целых числа, подзапрос мог бы быть миллионами строк задолго до того, как это произошло, но подзапрос select * в вашем первом запросе мог бы столкнуться с этой проблемой гораздо раньше.

3 голосов
/ 11 февраля 2009

Я думаю, что это сделало бы это, но я не уверен, что он лучший или самый быстрый.

SELECT * FROM table 
WHERE (id, version_id) IN 
  (SELECT id, MAX(version_id) FROM table GROUP BY id)
2 голосов
/ 11 февраля 2009
SELECT id, version_id, field1, field2
FROM (
    SELECT @prev = id AS st, (@prev := id), m.*
    FROM (
           (SELECT @prev := NULL) p,
           (
            SELECT *
            FROM   mytable
            ORDER BY
                   id DESC, version_id DESC
           ) m
     ) m2
WHERE NOT IFNULL(st, FALSE);

Нет подзапросов, один проход на UNIQUE INDEX ON MYTABLE (id, version_id), если у вас есть один (что я думаю, вы должны)

0 голосов
/ 30 июля 2018

Всегда можно пойти и на аналитические функции, которые дадут вам больше контроля

select tmp.* from ( select id,version_id,field1,field2, rank() over(partition by id order by version_id desc ) as rnk from table) tmp where tmp.rnk=1

Если у вас возникли проблемы с функцией rank () в зависимости от типа данных, то вы также можете выбрать из row_number () или dens_rank ().

0 голосов
/ 23 июля 2012

Этот запрос выполнит работу без группы по:

SELECT * FROM table AS t
LEFT JOIN table AS t2 
    ON t.id=t2.id 
    AND t.version_id < t2.version_id
WHERE t2.id IS NULL

Не нужны никакие временные таблицы.

0 голосов
/ 09 мая 2009

не проверял, но что-то вроде этого может работать:

SELECT * FROM таблицы GROUP BY id ПОРЯДОК МАКС. (Version_id) DESC

0 голосов
/ 11 февраля 2009

Я думаю, это то, что вы хотите.

select id, max(v_id), field1, field2 from table group by id

Полученные результаты

1, 2, textb, text2

2, 3, текст, текст5

Изменить: Я пересоздал таблицу и вставил те же данные с идентификатором version_id, являющимся составным первичным ключом Это дало ответ, который я дал ранее. Это было также в MySQL.

0 голосов
/ 11 февраля 2009

Обычно я делаю это с помощью подзапроса:

выберите id, version_id, field1, field2 из datatable как dt, где id = (выберите id из datatable, где id = dt.id, порядок по предельному значению version_id 1)

0 голосов
/ 11 февраля 2009

Это псевдокод, но что-то вроде этого должно работать нормально

select *
from table
inner join
(
    select id , max(version_id) maxVersion
    from table 
) dvtbl ON id = dvtbl.id && versionid = dvtbl.maxVersion
...