SQL LIMIT для получения последних записей

Я пишу скрипт, который будет перечислять 25 элементов всех 12 категорий. Структура базы данных такая:

tbl_items
---------------------------------------------
item_id | item_name | item_value | timestamp 
---------------------------------------------

tbl_categories
-----------------------------
cat_id | item_id | timestamp
-----------------------------

В таблице tbl_items около 600 000 строк. Я использую этот SQL-запрос:

SELECT e.item_id, e.item_value
  FROM tbl_items AS e
  JOIN tbl_categories AS cat WHERE e.item_id = cat.item_id AND cat.cat_id = 6001
  LIMIT 25

Используя тот же запрос в цикле для cat_id от 6000 до 6012. Но мне нужны последние записи каждой категории. Если я использую что-то вроде:

SELECT e.item_id, e.item_value
  FROM tbl_items AS e
  JOIN tbl_categories AS cat WHERE e.item_id = cat.item_id AND cat.cat_id = 6001
  ORDER BY e.timestamp
  LIMIT 25

... запрос обрабатывается примерно 10 минут, что неприемлемо. Могу ли я использовать LIMIT более красиво, чтобы получить последние 25 записей для каждой категории?

Может ли кто-нибудь помочь мне добиться этого без ORDER BY? Любые идеи или помощь будут высоко оценены.

ИЗМЕНИТЬ

tbl_items

+---------------------+--------------+------+-----+---------+-------+
| Field               | Type         | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| item_id             | int(11)      | NO   | PRI | 0       |       |
| item_name           | longtext     | YES  |     | NULL    |       |
| item_value          | longtext     | YES  |     | NULL    |       |
| timestamp           | datetime     | YES  |     | NULL    |       |
+---------------------+--------------+------+-----+---------+-------+

tbl_categories

+----------------+------------+------+-----+---------+-------+
| Field          | Type       | Null | Key | Default | Extra |
+----------------+------------+------+-----+---------+-------+
| cat_id         | int(11)    | NO   | PRI | 0       |       |
| item_id        | int(11)    | NO   | PRI | 0       |       |
| timestamp      | datetime   | YES  |     | NULL    |       |
+----------------+------------+------+-----+---------+-------+

person Astha    schedule 17.01.2012    source источник
comment
Пожалуйста, покажите свои операторы создания таблицы. Ваши индексы правильные?   -  person Artem Goutsoul    schedule 17.01.2012
comment
@Astha - Пожалуйста, не могли бы вы подтвердить, какие индексы у вас есть, и можете ли вы запросить/создать новые?   -  person MatBailie    schedule 17.01.2012
comment
Из вопроса не ясно, должен ли весь результат быть упорядочен по отметке времени или данным внутри каждой группы?   -  person newtover    schedule 17.01.2012


Ответы (3)


Прежде всего:

Кажется, это отношение N:M между items и categories: item может быть в нескольких categories. Я говорю это, потому что categories имеет item_id внешний ключ.

Если это не отношение N:M, вам следует подумать об изменении дизайна. Если это отношение 1:N, где категория имеет несколько элементов, то item должен содержать category_id внешний ключ.

Работа с Н:М:

Я переписал ваш запрос, чтобы сделать внутреннее соединение вместо перекрестного:

  SELECT e.item_id, e.item_value
  FROM 
     tbl_items AS e
  JOIN 
     tbl_categories AS cat 
        on e.item_id = cat.item_id
  WHERE  
     cat.cat_id = 6001
  ORDER BY 
     e.timestamp
  LIMIT 25

Для оптимизации производительности необходимы следующие индексы:

create index idx_1 on tbl_categories( cat_id, item_id)

это не обязательно индекс для элементов, потому что первичный ключ также индексируется. Индекс, который содержит отметку времени, не очень помогает. Чтобы быть уверенным, можно попробовать использовать индекс для элемента с item_id и timestamp, чтобы избежать доступа к таблице и брать значения из индекса:

create index idx_2 on tbl_items( item_id, timestamp)

Чтобы повысить производительность, вы можете изменить свой цикл по категориям одним запросом:

  select T.cat_id, T.item_id, T.item_value from 
  (SELECT cat.cat_id, e.item_id, e.item_value
   FROM 
     tbl_items AS e
   JOIN 
     tbl_categories AS cat 
        on e.item_id = cat.item_id
   ORDER BY 
     e.timestamp
   LIMIT 25
  ) T
  WHERE  
     T.cat_id between 6001 and 6012
  ORDER BY
     T.cat_id, T.item_id

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

person dani herrera    schedule 17.01.2012
comment
Спасибо за ответ. Я не могу изменить структуру БД, так как она не моя. но да, это таблицы отношений 1:N. один item_id будет только в одной категории. Я пробовал этот запрос, но он также занимает такое же количество времени. Я использую цикл для идентификаторов категорий, и я не могу использовать один запрос, подобный этому, с между калузами. надеюсь, вы поняли мою точку зрения. - person Astha; 17.01.2012
comment
Если это не ваша база данных, это непростое решение. Взгляните на этот связанный вопрос: Возможна ли межтабличная индексация? - person dani herrera; 17.01.2012

Можете ли вы добавить индексы? Если вы добавите индекс в timestamp и другие соответствующие столбцы, ORDER BY не займет 10 минут.

person Joni    schedule 17.01.2012
comment
Индекс похож на телефонную книгу. Если бы в телефонной книге не было порядка, поиск чьего-то телефонного номера занял бы часы. Индекс в столбце меток времени сообщает MySQL, где расположены записи, по меткам времени, поэтому он может искать строки, как в телефонной книге. - person toon81; 17.01.2012
comment
К сожалению, я не знаю ни одного достойного веб-сайта, с которого можно было бы начать. (кто угодно?) - person toon81; 17.01.2012
comment
Ну, всегда есть официальная документация, хотя она может быть не лучшим введением в предмет: dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html - person Joni; 17.01.2012
comment
Да, в столбце меток времени нет индексов. Добавление индекса в столбец отметки времени таблицы элементов ускорит ваш ORDER BY в тысячи раз (и я не преувеличиваю). - person toon81; 17.01.2012
comment
Используйте B-дерево, это упорядоченный индекс. Хэш-индекс не упорядочен. - person edze; 17.01.2012
comment
Это может быть сложно, если вы только начинаете (это было для меня), но вы можете многому научиться. Если вы запустите запрос, который выглядит как EXPLAIN {sql-query}, MySQL сообщит вам, как он будет выполнять {sql-query}.dev.mysql.com/doc/refman/5.0/en/explain-output.html - person toon81; 17.01.2012

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

Поля BLOB и TEXT в MySQL в основном предназначены для хранения полных файлов, текстовых или двоичных. Они хранятся отдельно от данных строк для таблиц InnoDB. Каждый раз, когда запрос включает сортировку (явно или для group by), MySQL обязательно использует диск для сортировки (поскольку он не может быть уверен заранее, насколько велик какой-либо файл).

И это, вероятно, эмпирическое правило: если вам нужно вернуть более одной строки столбца в запросе, тип поля почти никогда не должен быть TEXT или BLOB, вместо этого используйте VARCHAR или VARBINARY.

UPD

Если вы не можете обновить таблицу, запрос вряд ли будет быстрым с текущими индексами и типами столбцов. Но, так или иначе, вот похожий вопрос и популярное решение вашей проблемы: Как ВЫБРАТЬ последние четыре элемента в категории?

person newtover    schedule 17.01.2012
comment
Спасибо за это объяснение. Очень признателен, но, как я уже сказал, эта база данных не моя. Мне нужно только получить данные, чтобы показать. - person Astha; 18.01.2012