MySQL-управление, какая строка возвращается группой по
у меня есть таблица базы данных, как это:
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
Если вы не разработали его, он содержит несколько версий строки, а затем некоторые текстовые данные.
Я хочу запросить его и вернуть версию с наибольшим числом для каждого идентификатора. (так что вторая и последняя строки только в приведенном выше).
Я пробовал использовать group by при заказе по version_id DESC-но он, кажется, заказывает после его группировки, поэтому это не работает.
у кого-нибудь есть идеи? Я не могу поверить, что это невозможно!
обновление:
придумайте это, что работает, но использует подзапрос:
SELECT *
FROM (SELECT * FROM table ORDER BY version_id DESC) t1
GROUP BY t1.id
9 ответов:
это называется выбором группового максимума столбца. вот несколько различных подходов для 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, но некоррелированные подзапросы не так уж плохи, пока они не настолько огромны, что они будут записываться на диск, а не в память. Учитывая, что в этом запросе есть только два int, подзапрос может составлять миллионы строк задолго до этого, но подзапрос select * в вашем первом запросе может пострадать от этой проблемы намного раньше.
Я думаю, что это сделает это, не уверен, что это лучший или самый быстрый, хотя.
SELECT * FROM table WHERE (id, version_id) IN (SELECT id, MAX(version_id) FROM table GROUP BY id)
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)если у вас есть один (который я думаю, вы должны)
обычно я делаю это с подзапросом:
выберите ID, version_id, поле1, поле2 из DataTable в качестве ДТ где ID = (выберите ID из объекта DataTable, где ID = ДТ.id заказа путем ограничения version_id убыв 1)
это псевдо-код, но что-то вроде этого должно работать нормально
select * from table inner join ( select id , max(version_id) maxVersion from table ) dvtbl ON id = dvtbl.id && versionid = dvtbl.maxVersion
этот запрос будет выполнять работу без группы by:
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ему не нужны никакие временные таблицы.
всегда можно перейти к аналитическим функциям, которые дадут вам больше контроля
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() или dense_rank () тоже.
Я думаю, что это то, что вы хотите.
select id, max(v_id), field1, field2 from table group by idрезультаты, которые я получаю, являются
1, 2, textb, text2
2, 3, texte, text5
Edit: Я воссоздал таблицу и вставил те же данные с идентификатором version_id, являющимся составным первичным ключом. Это дало ответ, который я дал ранее. Это было также в MySQL.
Не проверял, но что-то подобное может работать:
выберите * из группы таблиц по порядку id по MAX (version_id) DESC
Comments