SQL LIMIT для получения последних записей - PullRequest
1 голос
/ 17 января 2012

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

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

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

В таблице около 600 000 строк tbl_items. Я использую этот запрос 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? Любые идеи или помощь будут высоко оценены.

EDIT

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    |       |
+----------------+------------+------+-----+---------+-------+

Ответы [ 3 ]

1 голос
/ 17 января 2012

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

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

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

Работа с N: M:

Я переписал вашзапрос для создания внутреннего объединения, устанавливающий перекрестное соединение:

  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)

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

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

1 голос
/ 17 января 2012

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

Поля

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

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

UPD

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

1 голос
/ 17 января 2012

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

...