Оптимизация MySQL GROUP BY или DISTINCT для больших представлений - PullRequest
4 голосов
/ 10 февраля 2009

Рассмотрим представление, состоящее из нескольких таблиц ... например, v_active_car, который состоит из таблиц car, соединенных с body, engine, wheels и stereo. Это может выглядеть примерно так:

v_active_cars просмотр

SELECT * FROM car
    INNER JOIN body ON car.body = body.body_id
    INNER JOIN engine ON car.engine = engine.engine_id
    INNER JOIN wheels ON car.wheels = wheels.wheels_id
    INNER JOIN stereo ON car.stereo = stereo.stereo_id
    WHERE car.active = 1
    AND engine.active = 1
    AND wheels.active = 1
    AND stereo.active = 1

Каждый компонент автомобиля имеет «активный» флаг. Теперь мне нужно найти все стереосистемы, доступные в активных автомобилях. Для этого нужно использовать весь вид, а не только таблицу stereo - просто потому, что стерео активно, это не значит, что оно доступно в автомобиле.

Так что я могу сделать

SELECT DISTINCT stereo_id FROM v_active_cars

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

Я пробовал это, но это еще медленнее:

SELECT stereo_id FROM stereo WHERE EXISTS
(SELECT 1 FROM v_active_cars WHERE stereo_id = stereo.stereo_id)

Могу ли я еще что-нибудь сделать, чтобы сделать это быстрее?

Ответы [ 4 ]

1 голос
/ 10 февраля 2009
  1. убедитесь, что есть индексы для всех JOIN
    • в вашем случае, каждый уровень выбирается как ключом, так и флагом. добавление флага как части индекса может позволить БД использовать только индекс вместо чтения всей записи
    • убедитесь, что у вас достаточно ОЗУ для хранения результатов. В частности, в таблицах InnoDB есть множество регуляторов, которые вам нужно настроить. большинство значений по умолчанию предполагают очень старое оборудование и слишком мало ОЗУ.
1 голос
/ 10 февраля 2009

Кажется, ты все делаешь правильно. Следующим шагом будет проверка покрытия индекса.

0 голосов
/ 10 февраля 2009

Вы можете попробовать создать представление для каждой части, показывающее только активные, а затем присоединиться к ним. например.

VIEW activeCar
SELECT * FROM car WHERE car.active = 1

VIEW activeEngine
SELECT * FROM engine WHERE engine.active = 1

Тогда ваш окончательный вид может быть

SELECT * FROM activeCar
INNER JOIN activeEngine ON activeCar.engine = activeEngine.engine_id

Очевидно, что у вас есть индекс активного столбца.

Другая альтернатива - иметь индекс как для идентификатора, так и для активного флага. Затем вы можете выполнить active = 1 при присоединении. Таким образом, для соединения используется только один индекс, а не один для идентификатора и один для активного.

SELECT * FROM car
INNER JOIN body ON car.body = body.body_id AND body.active = 1
INNER JOIN engine ON car.engine = engine.engine_id AND engine.active = 1
INNER JOIN wheels ON car.wheels = wheels.wheels_id AND wheels.active = 1
INNER JOIN stereo ON car.stereo = stereo.stereo_id AND stereo.active = 1
0 голосов
/ 10 февраля 2009

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

SELECT stereo_id
FROM stereo s, (
  SELECT *
  FROM v_active_cars
  ORDER BY stereo_id
  ) v
WHERE s.active = 1
  AND v.stereo = s.stereo_id

ORDER BY здесь должно препятствовать продвижению предиката в представление, а оптимизатор должен выбрать хеш-соединение.

...