Получить последний отчетливый набор записей
У меня есть таблица базы данных, содержащая следующие столбцы:
id code value datetime timestamp
В этой таблице единственные уникальные значения находятся в id, то есть в первичном ключе.
Я хочу получить последний отчетный набор записей в этой таблице на основе значения datetime. Например, скажем ниже, моя таблица
id code value datetime timestamp
1 1023 23.56 2011-04-05 14:54:52 1234223421
2 1024 23.56 2011-04-05 14:55:52 1234223423
3 1025 23.56 2011-04-05 14:56:52 1234223424
4 1023 23.56 2011-04-05 14:57:52 1234223425
5 1025 23.56 2011-04-05 14:58:52 1234223426
6 1025 23.56 2011-04-05 14:59:52 1234223427
7 1024 23.56 2011-04-05 15:00:12 1234223428
8 1026 23.56 2011-04-05 15:01:14 1234223429
9 1025 23.56 2011-04-05 15:02:22 1234223430
Я хочу получить записи с идентификаторами 4, 7, 8 и 9, т.е. последний набор записей с разными кодами (на основе значения datetime). То, что я выделил, является просто примером того, чего я пытаюсь достичь, поскольку эта таблица в конечном итоге будет содержать миллионы записей и сотни отдельных значений кода.
Какой SQL-оператор можно использовать для достижения этого? Кажется, я не могу сделать это с помощью одного оператора SQL. Моя база данных - это MySQL 5.
Ответы
Ответ 1
Это должно сработать для вас.
SELECT *
FROM [tableName]
WHERE id IN (SELECT MAX(id) FROM [tableName] GROUP BY code)
Если id является AUTO_INCREMENT, нет необходимости беспокоиться о времени, которое намного дороже для вычисления, так как последнее время datetime также будет иметь самый высокий идентификатор.
Обновление:. С точки зрения производительности убедитесь, что столбцы id
и code
индексируются при работе с большим количеством записей. Если id
является основным ключом, он встроен, но вам может потребоваться добавить некластеризованный индекс, охватывающий code
и id
.
Ответ 2
Попробуйте следующее:
SELECT *
FROM <YOUR_TABLE>
WHERE (code, datetime, timestamp) IN
(
SELECT code, MAX(datetime), MAX(timestamp)
FROM <YOUR_TABLE>
GROUP BY code
)
Ответ 3
Это и старое сообщение, но тестирование @smdrager на большие таблицы было очень медленным. Мое исправить это было использование "внутреннего соединения" вместо "где в".
SELECT *
FROM [tableName] as t1
INNER JOIN (SELECT MAX(id) as id FROM [tableName] GROUP BY code) as t2
ON t1.id = t2.id
Это работало очень быстро.
Ответ 4
Я попробую что-то вроде этого:
select * from table
where id in (
select id
from table
group by code
having datetime = max(datetime)
)
(отказ от ответственности: это не проверено)
Если строка с большим значением datetime также имеет больший id, решение, предлагаемое smdrager, выполняется быстрее.
Ответ 5
Похоже, что все существующие ответы предлагают сделать GROUP BY code
на всю таблицу. Когда это логически корректно, в действительности этот запрос будет проходить через всю (!) Таблицу (чтобы убедиться, используйте EXPLAIN
). В моем случае у меня в таблице менее 500 тыс. Строк и выполняется ...GROUP BY code
занимает 0,3 секунды, что абсолютно недопустимо.
Однако я могу использовать знания моих данных здесь (читайте как "показывать последние комментарии к сообщениям"):
- Мне нужно выбрать только топ-20 записей
- Количество записей с одинаковым кодом в последних X записях относительно невелико
- Общее количество записей >> количество доступного
code
>> количество "лучших" записей, которые вы хотите получить
Экспериментируя с числами, я обнаружил, что всегда могу найти 20 разных code
если выберу только последние 50 записей. И в этом случае работает следующий запрос (имея в виду комментарий @smdrager о высокой вероятности использования id
вместо datetime
)
SELECT id, code
FROM tablename
ORDER BY id DESC
LIMIT 50
Выбор только последних 50 записей очень быстр, потому что не нужно проверять всю таблицу. А остальное - выбрать топ-20 с отличным code
из этих 50 записей.
Очевидно, что запросы для набора из 50 (100, 500) элементов выполняются значительно быстрее, чем для всей таблицы с сотнями тысяч записей.
Необработанный SQL "Постобработка"
SELECT MAX(id) as id, code FROM
(SELECT id, code
FROM tablename
ORDER BY id DESC
LIMIT 50) AS nested
GROUP BY code
ORDER BY id DESC
LIMIT 20
Бэкэнд-сторона "Постобработка"
И после этого вам нужно обработать данные на вашем языке программирования, чтобы включить в окончательный набор только записи с отдельным code
.
Какой-то псевдокод Python:
records = select_simple_top_records(50)
added_codes = []
top_records = []
for record in records:
# If record for this code was already found before
# Note: this is not optimal, better to use structure allowing O(1) search and insert
if record['code'] in added_codes:
continue
# Save record
top_records.append(record)
added_codes.append(record['code'])
# If we found all top-20 required, finish
if len(top_records) >= 20:
break