Выборка сложных списков из большого числа таблиц. Оптимизация.

WhatEverMD

Новичок
Выборка сложных списков из большого числа таблиц. Оптимизация.

Всем доброго времени суток :)

Есть список сущностей, скажем событий(пример будет значительно упрощён чтобы передать суть).
У каждого события есть "массив" категорий в которые она может попасть(на самом деле у него есть ещё
огромная куча параметров, каждый из которых представляет собой список разных элементов).

В БД это выглядит примерно так так:

----------
events
----------
idEvent
....

----------------------------
events_category_sets
----------------------------
idEvent
idCat
....

-------------
categories
-------------
idCat
catName
....


То есть одно событие может попасть в разное количество категорий.
Необходимо получить список событий со всеми его под списками. В данном примере
со всеми категориями в которые оно попадает.

На данный момент это реализовано следующим образом:
Выбираются все события удовлетворяющее определённому критерию:

SELECT E.eventId, C.idCat, C.catName
FROM events AS E
LEFT JOIN events_category_sets AS ECS ON ECS.idEvent = E.idEvent
LEFT JOIN categories AS C ON C.idCat = ECS.idCat
WHERE -- тут какое нибудь условие
GROUP BY E.event_id
ORDER BY idEvent -- на самом деле поле по которому идёт упорядочение тоже указывается переменной из php
LIMIT -- тут переменными из php указывается с какое по какое выбирать
-- так как реализован постраничный просмотр

А потом в php цикл пробегает по результату и "добирает" все остальные под списки.
(Это например контактные телефоны, афиши, разделы... их на самом деле много).
На маленькой БД в 800 событий такой подход уже начинает значительно тормозить.
В проекте планируется значительно большее количество событий.

Насколько я понимаю, тормозит запрос по двум причинам:
- много дополнительных запросов в цикле
- много конструкций типа LEFT JOIN так как часто критерии выборки лежат не в основной таблице во вспомогательных.

Первым альтернативным вариантом что приходит в голову:
Всё выбрать одним запросом добавляя нужные поля JOIN'ами а потом обработать полученный результат в php в скриптах.
Ведь в результате такого запроса будет большое количество повторяющихся строк, отличающихся лишь одним столбцом.
Но это тоже не решение так как на большом количестве выбираемых событий запрос работает ещё дольше.

Но так как мне нужно выбирать ограниченное количество(от 20 до 100) то всё же такой подход чуточку быстрее.
Я тестировал на той же БД. Но возникает другая проблема. Я не могу воспользоваться LIMIT'ом для ограничения выборки,
потому что оно считает повторяющиеся события. Т.е. строки в результирующей таблице которые содержат одни и те же
idEvent но отличаются где то в других столбцах. Может как то можно заставить считать лишь уникальные idEvent по которым
идёт LIMIT?
Можно конечно предварительно выбрать уникальные idEvent соответствующие
критериям запроса, и уже вторым запросом выбрать все поля, добавив в проверку что idEvent
входит в один из тех что были получены (из вспомогательных таблиц добавляя их JOIN'ом). Но как мне кажется
это будет тормозить вобще ужасно жестоко :)

К сожалению возможность тестирования в коде проекта осложнена тем что сами исходники достаточно запутаны и плохо упорядоченны.
К тому же запрос "генерируется на ходу" в зависимости от фильтров. Так что буду благодарен за любую подсказку или совет.

Каким образом решаются подобные задачи ? Ведь наверняка она встречается достаточно часто.
Спасибо.
 

x-yuri

Новичок
Может как то можно заставить считать лишь уникальные idEvent по которым
идёт LIMIT?
можно найти список событий ограниченный с помощью LIMIT написав вложенный запрос
 

clevel

Новичок
Несколько раз с таким сталкивался. Для себя понял следующее:

1. Выбираем из главной таблицы основные ID
2. Потом делаем пару запросов (не в цикле!) в подтаблицы, чтобы на основе массива ID с первого запроса вытащить варианты подсписков.
3. В пхп потом циклом проходимся по массиву с первого запроса, и формируем выходную табличку.

В чем фишка: когда у нас один запрос с объединениями, мускуль сначала делает эти объединения (без учета LIMIT), потом уже начинает делать выборку. В итоге скорость падает.
При коротких запросах - выбор по списку ID - все работает гораздо быстрее.
 

x-yuri

Новичок
clevel но то же самое можно одним запросом сделать. Только не знаю, что лучше (быстрее)
 
Сверху