Получить последний отчетливый набор записей - PullRequest
42 голосов
/ 05 апреля 2011

У меня есть таблица базы данных, содержащая следующие столбцы:

id   code   value   datetime   timestamp

В этой таблице единственные уникальные значения находятся в идентификаторе, то есть первичном ключе.

Я хочу получить последний отдельный набор записей в этой таблице на основе значения 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, т. Е. Последний набор записей с разными кодами (на основе значения даты и времени). То, что я выделил, является просто примером того, чего я пытаюсь достичь, так как эта таблица в конечном итоге будет содержать миллионы записей и сотни отдельных значений кода.

Какой оператор SQL я могу использовать для достижения этой цели? Я не могу сделать это с помощью одного оператора SQL. Моя база данных - MySQL 5.

Ответы [ 5 ]

69 голосов
/ 05 апреля 2011

Это должно работать для вас.

 SELECT * 
 FROM [tableName] 
 WHERE id IN (SELECT MAX(id) FROM [tableName] GROUP BY code)

Если для идентификатора установлено значение AUTO_INCREMENT, нет необходимости беспокоиться о дате и времени, которые намного дороже вычислить, поскольку самые последние даты и время также будут иметь самый высокий идентификатор..

Обновление: С точки зрения производительности убедитесь, что столбцы id и code проиндексированы при работе с большим количеством записей.Если id является первичным ключом, он встроен, но вам может потребоваться добавить некластеризованный индекс, охватывающий code и id.

7 голосов
/ 05 апреля 2011

Попробуйте это:

SELECT * 
  FROM <YOUR_TABLE>
 WHERE (code, datetime, timestamp) IN
 (
   SELECT code, MAX(datetime), MAX(timestamp)
     FROM <YOUR_TABLE>
    GROUP BY code
 )
3 голосов
/ 31 мая 2017

Это и старый пост, но тестирование ответа @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

Это работало очень быстро.

1 голос
/ 05 апреля 2011

Я попробую что-то вроде этого:

select * from table
where id in (
    select id
    from table
    group by code
    having datetime = max(datetime)
)

(отказ от ответственности: это не проверено)

Если строка с большей датой и временем имеет больший идентификатор, решение, предложенное smdrager, будет быстрее.

0 голосов
/ 03 апреля 2019

Похоже, что все существующие ответы предлагают сделать GROUP BY code на всю таблицу. Когда это логически правильно, в действительности этот запрос будет проходить через всю (!) Таблицу (чтобы убедиться, используйте EXPLAIN). В моем случае в таблице менее 500 тыс. Строк, и выполнение ...GROUP BY code занимает 0,3 секунды, что абсолютно недопустимо.

Однако я могу использовать знание своих данных здесь (читается как «показывать последние комментарии к сообщениям»):

  • Мне нужно выбрать только 20 лучших записей
  • Количество записей с одинаковым кодом в последних X записях относительно невелико
  • Общее количество записей >> количество доступных code s >> количество «лучших» записей, которые вы хотите получить

Экспериментируя с числами, я обнаружил, что всегда могу найти 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

Это даст вам список действительно быстрых id, и, если вы хотите выполнить дополнительные JOIN, поместите этот запрос как еще один вложенный запрос и выполните все соединения с ним.

Внутренняя сторона "Постобработка"

И после этого вам нужно обработать данные на вашем языке программирования, чтобы включить в окончательный набор только записи с различными 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
...