2013-04-30 5 views
-1

У меня есть сайт медиацентра, на котором я работаю, у него есть страница, отображающая категории мультимедиа. Каждая категория может иметь несколько назначенных ей элементов мультимедиа, и каждый элемент мультимедиа может быть присвоен нескольким категориям.Как упростить этот сложный запрос?

Страница содержит текстовый ввод, который должен иметь возможность фильтровать категории, которые показаны.

Что мне нужно сделать, это получить каждую категорию, связанную с текущим пользователем ($user_id), а также элемент мультимедиа, принадлежащий этому пользователю (неиспользуемые категории не отображаются). Обычно это было бы достаточно просто, но я также должен иметь возможность фильтровать категории на основе полей в других таблицах, связанных с носителем.

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

  • message_number в media таблице
  • keywords в media таблице
  • speaker_name в media_speakers таблице
  • series_namemedia_series стол
  • book_name в media_books таблице
  • category_name в media_categories таблице

Как теперь, запрос занимает несколько секунд. Я не профессионал MySQL, поэтому я уверен, что должны быть лучшие способы сделать то, что мне нужно сделать здесь. В случае, если это помогает, я использую MySQLi через PHP. У моего запроса есть несколько подзапросов, и я уверен, что это причина проблемы, но я не знал другого способа делать то, что я пытаюсь сделать.

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

media стол (опуская некоторые нерелевантные поля) (серия, динамик, и книжные поля содержат идентификатор записи в их соответствующих таблицах):

`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) DEFAULT NULL, 
    `date` date NOT NULL DEFAULT '0000-00-00', 
    `message_number` varchar(32) DEFAULT NULL, 
    `series` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000', 
    `speaker` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000', 
    `book` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000', 
    `keywords` text NOT NULL, 
    PRIMARY KEY (`id`) 

media_series стол:

`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) DEFAULT NULL, 
    `series_name` varchar(255) NOT NULL DEFAULT '', 
    `cover` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 

media_speakers стол:

`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) DEFAULT NULL, 
    `speaker_name` varchar(255) NOT NULL DEFAULT '', 
    `cover` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 

media_books стола:

`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT, 
    `book_name` varchar(64) NOT NULL DEFAULT '', 
    `book_shortname` varchar(10) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`) 

media_categories стола:

`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) NOT NULL, 
    `category_name` varchar(255) NOT NULL DEFAULT '', 
    `cover` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `id` (`id`) 

media_categories_assoc стола:

`id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` int(10) unsigned DEFAULT NULL, 
    `media_id` int(10) unsigned zerofill DEFAULT NULL, 
    `category_id` int(10) unsigned zerofill DEFAULT NULL, 
    `marked_for_deletion` int(1) DEFAULT '0', 
    PRIMARY KEY (`id`) 

Наконец, чрезмерно сложные запросы:

SELECT media_categories.id   `media_categories.id`, 
     media_categories.user_id  `media_categories.user_id`, 
     media_categories.category_name `media_categories.category_name`, 
     media_categories.cover   `media_categories.cover`, 
     (SELECT id 
     FROM media 
     WHERE user_id = '$user_id' 
       AND media_categories.id IN (SELECT category_id 
              FROM media_category_assoc 
              WHERE user_id = '$user_id') 
     ORDER BY `date` DESC 
     LIMIT 1)      `media.id`, 
     (SELECT `date` 
     FROM media 
     WHERE user_id = '$user_id' 
       AND media_categories.id IN (SELECT category_id 
              FROM media_category_assoc 
              WHERE user_id = '$user_id') 
     ORDER BY `date` DESC 
     LIMIT 1)      `media.date`, 
     (SELECT series 
     FROM media 
     WHERE user_id = '$user_id' 
       AND media_categories.id IN (SELECT category_id 
              FROM media_category_assoc 
              WHERE user_id = '$user_id') 
     ORDER BY `date` DESC 
     LIMIT 1)      `media.series`, 
     (SELECT speaker 
     FROM media 
     WHERE user_id = '$user_id' 
       AND media_categories.id IN (SELECT category_id 
              FROM media_category_assoc 
              WHERE user_id = '$user_id') 
     ORDER BY `date` DESC 
     LIMIT 1)      `media.speaker` 
FROM media_categories 
     LEFT JOIN media 
       ON media.id IN (SELECT media_id 
           FROM media_category_assoc 
           WHERE media_id = media.id 
            AND user_id = '$user_id') 
     LEFT JOIN media_series 
       ON media.series = media_series.id 
     LEFT JOIN media_speakers 
       ON media.speaker = media_speakers.id 
     LEFT JOIN media_books 
       ON media.book = media_books.id 
WHERE media_categories.user_id = '$user_id' 
     AND media_categories.id IN (SELECT category_id 
            FROM media_category_assoc 
            WHERE user_id = '$user_id') 
     AND (media.title LIKE '%filter_text%' 
       OR media.message_number LIKE '%filter_text%' 
       OR media.keywords LIKE '%filter_text%' 
       OR media_speakers.speaker_name LIKE '%filter_text%' 
       OR media_categories.category_name LIKE '%filter_text%' 
       OR media_series.series_name LIKE '%filter_text%' 
       OR media_books.book_name LIKE '%filter_text%') 
GROUP BY `media_categories.id` 
ORDER BY `media.date` DESC 
LIMIT 0, 12; 
+0

Подзапросы действительно могут заставлять ваш запрос быть настолько медленным. Попробуйте подход «разделяй и властвуй»: создайте временные таблицы для «промежуточных» шагов (подзапросов), а затем соедините их ... Вы можете поместить все создание временных таблиц в хранимую процедуру, которая поможет вам определить каждый параметр запроса только один раз. Обязательно создайте все необходимые индексы в каждой временной таблице. – Barranka

+0

Хммм ... Спасибо за подсказку. Временные таблицы звучат так, как будто это может быть полезно. Однако я не совсем уверен, как это реализовать здесь, и я не уверен, что вы подразумеваете под «необходимыми индексами». – vertigoelectric

+0

Как мой вопрос вне темы? – vertigoelectric

ответ

1

Как я упоминал в своем комментарии, подзапросы могут быть узкими местами в вашем запросе. Прежде всего, запустите explain select... по вашему запросу, чтобы проверить план выполнения.

Смотрите справочное руководство:

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

Вы можете использовать это:

SELECT 
..., 
(SELECT id 
    FROM media 
    WHERE user_id = '$user_id' 
      AND media_categories.id IN (SELECT category_id 
             FROM media_category_assoc 
             WHERE user_id = '$user_id') 
    ORDER BY `date` DESC 
    LIMIT 1), 
.... 

И вы можете сделать что-то вроде этого:

drop table if exists temp_step1; 
create temporary table temp_step1 
    select id 
    from media 
    where user_id = @user_id -- I'm assuming you are putting this in a stored procedure 
     and media_categories.id in (SELECT category_id 
            FROM media_category_assoc 
            WHERE user_id = @user_id) 
    order by `date` desc 
    limit 1; 

Затем вы можете использовать эту temp_step1 таблицу в качестве источника строк для вашего большого запроса.

Обратите внимание, что этот пример возвращает только одну строку, поэтому нет смысла индексировать это. Для тех временных таблиц, которые содержат более одной строки и которые вы используете в предложении FROM ... JOIN ... вашего запроса, вам нужно будет создать индексы , по крайней мере, во всех полях, которые вы делаете в соединениях. Чтобы сделать это, после создания временной таблицы (например temp_step_X), вы должны сделать это:

alter table temp_step_X 
    add index idx_indexName(field1), 
    ...; 

Надеется, что это помогает вам

+0

Я ценю время, которое вы взяли, чтобы объяснить это. Я посмотрю, смогу ли я реализовать это в своем коде позже после того, как мой мозг отдохнул. Спасибо, что получил этот ответ, прежде чем мой вопрос был несправедливо закрыт как «вне темы». – vertigoelectric

+1

@vertigoelectric Ваш вопрос может быть лучше подходит для http://dba.stackexchange.com. Будьте внимательны: перекрестная проводка не рекомендуется (т.размещение одного и того же вопроса на разных сайтах stackexchange) – Barranka

+0

Перекрестная регистрация отлично до тех пор, пока вы позволяете всем знать, что вы делаете. – Strawberry

1

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

Возможно, это может помочь?

SELECT media_categories.id, 
     media_categories.user_id, 
     media_categories.category_name, 
     media_categories.cover, 
     newest_media.id, 
     newest_media.'date', 
     newest_media.series, 
     newest_media.speaker 
FROM media_categories 
     LEFT JOIN media_category_assoc 
       ON media_categories.id = media_category_assoc.category_id AND media_categories.user_id = media_category_assoc.user_id 
     LEFT JOIN (
        SELECT id, 'date', series, speaker 
        FROM media 
        WHERE media.id = media_category_assoc.media_id 
        ORDER BY `date` DESC 
        LIMIT 1 
       ) newest_media ON newest_media.user_id = '$user_id' 
     LEFT JOIN media_series 
       ON newest_media.series = media_series.id 
     LEFT JOIN media_speakers 
       ON newest_media.speaker = media_speakers.id 
     LEFT JOIN media_books 
       ON newest_media.book = media_books.id 
     LEFT JOIN media 
       ON media.id = media_category_assoc.media_id AND media.user_id = '$user_id' 
WHERE media_categories.user_id = '$user_id' 
     AND (media.title LIKE '%filter_text%' 
       OR media.message_number LIKE '%filter_text%' 
       OR media.keywords LIKE '%filter_text%' 
       OR media_speakers.speaker_name LIKE '%filter_text%' 
       OR media_categories.category_name LIKE '%filter_text%' 
       OR media_series.series_name LIKE '%filter_text%' 
       OR media_books.book_name LIKE '%filter_text%') 
GROUP BY `media_categories.id` 
ORDER BY `media.date` DESC 
LIMIT 0, 12; 
Смежные вопросы