SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 12.03.2012 09:53:24

frm
Участник
Зарегистрирован: 27.09.2011
Сообщений: 8

Помогите с выборкой и ее оптимизацией

Доброго дня и сразу к сути вопроса.
Имеем следующие таблицы форума (некоторые, не имеющие значения в контексте данного вопроса поля я убрал в пользу наглядности):


mysql> describe module_forum_subsection; (Раздел форума)
+--------------------------+---------------+------+-----+---------+----------------+
| Field                    | Type          | Null | Key | Default | Extra          |
+--------------------------+---------------+------+-----+---------+----------------+
| id_subsection            | int(11)       | NO   | PRI | NULL    | auto_increment |
| id_section               | int(11)       | NO   | MUL | NULL    |                |
| name_subsection          | varchar(1500) | NO   |     | NULL    |                |
| preview_subsection       | varchar(3000) | YES  |     | NULL    |                |
| text_topic               | text          | YES  |     | NULL    |                |
| created                  | datetime      | NO   |     | NULL    |                |
| updated                  | datetime      | YES  |     | NULL    |                |
| id_user                  | int(11)       | NO   | MUL | NULL    |                |
| active                   | enum('Y','N') | YES  |     | Y       |                |
| position                 | int(11)       | NO   |     | NULL    |                |
| avatar                   | varchar(1500) | YES  |     | NULL    |                |
| accessTo                 | tinyint(1)    | YES  |     | 1       |                |
+--------------------------+---------------+------+-----+---------+----------------+

mysql> describe module_forum_topic; (Тема раздела форума)
+----------------------+----------------------+------+-----+---------+----------------+
| Field                | Type                 | Null | Key | Default | Extra          |
+----------------------+----------------------+------+-----+---------+----------------+
| id_topic             | int(11)              | NO   | PRI | NULL    | auto_increment |
| id_section           | int(11)              | NO   | MUL | NULL    |                |
| id_subsection        | int(11)              | NO   | MUL | NULL    |                |
| name_topic           | varchar(1500)        | NO   |     | NULL    |                |
| created              | datetime             | NO   |     | NULL    |                |
| updated              | datetime             | YES  |     | NULL    |                |
| type                 | enum('default','ad') | YES  |     | default |                |
| id_user              | int(11)              | NO   | MUL | NULL    |                |
| user_login           | char(25)             | NO   |     | NULL    |                |
| delete               | enum('Y','N')        | YES  |     | N       |                |
| views                | int(11)              | YES  |     | 0       |                |
+----------------------+----------------------+------+-----+---------+----------------+

mysql> describe module_forum_post; (Пост темы форума)
+----------------------+------------------------+------+-----+---------+----------------+
| Field                | Type                   | Null | Key | Default | Extra          |
+----------------------+------------------------+------+-----+---------+----------------+
| id_post              | int(11)                | NO   | PRI | NULL    | auto_increment |
| id_section           | int(11)                | NO   | MUL | NULL    |                |
| id_subsection        | int(11)                | NO   | MUL | NULL    |                |
| id_topic             | int(11)                | NO   | MUL | NULL    |                |
| post                 | text                   | NO   |     | NULL    |                |
| post_bbcode          | text                   | NO   |     | NULL    |                |
| id_user              | int(11)                | NO   | MUL | NULL    |                |
| created              | datetime               | NO   |     | NULL    |                |
| updated              | datetime               | YES  |     | NULL    |                |
| delete               | enum('Y','N')          | YES  |     | N       |                |
| type                 | enum('default','main') | YES  |     | default |                |
+----------------------+------------------------+------+-----+---------+----------------+
Задача: вывести информацию о разделе форума в формате(название раздела; количество тем раздела; количество ответов по всем темам; последнее сообщение раздела в формате название темы, дата, id поста). Условия выборки: темы должны быть не удаленные (параметр delete = 'N'); при подсчете количества ответов учитывать только посты типа type='default', а при выводе последнего сообщения посты любого типа.
Без последнего сообщения вывожу так:

SELECT
    `mfs`.*,
    count(distinct mft.id_topic) AS `topics`,
    count(mfp.id_post) AS `posts`
FROM
    `module_forum_subsection` AS `mfs`
LEFT JOIN
    `module_forum_topic` AS `mft`
    ON mfs.id_subsection = mft.id_subsection
    and mft.delete = 'N'
LEFT JOIN
    `module_forum_post` AS `mfp`
    ON mfp.id_topic = mft.id_topic
    and mfp.type = 'default'

WHERE
    (mfs.id_section = 11) /* Разделы на самом деле являются подразделами, но это не важно в данном случае */
    AND (mfs.active = 'Y')

Запрос достаточно шустрый все норма, а вот как теперь достать последнее сообщение, да так что бы не перегрузить запрос четырехэтажными вложениями? Я получил в принципе требуемый результат, но работает мой запрос недопустимо долго, вот он:

SELECT
    `mfs`.*,
    count(distinct mft.id_topic) AS `topics`,
    count(distinct mfp.id_post) AS `posts`,
    `last`.`id_post` AS `id_post_last`,
    `last`.`name_topic` AS `name_topic_last`,
    `last`.`created` AS `created_last`,
    `last`.`id_topic` AS `id_topic_last`,
    `last`.`login` AS `login_last`
FROM `module_forum_subsection` AS `mfs`
LEFT JOIN `module_forum_topic` AS `mft` ON mfs.id_subsection = mft.id_subsection and mft.delete = 'N'
LEFT JOIN `module_forum_post` AS `mfp` ON mfp.id_topic = mft.id_topic and mfp.type = 'default'
LEFT JOIN
    (select mft.id_subsection, mft.name_topic, mft.id_topic, mfp.id_post, mfp.created, mu.login
    from module_forum_topic mft
    left join module_forum_post mfp on mfp.id_topic = mft.id_topic
    left join module_users mu on mu.id = mfp.id_user
    where mft.delete = 'N' order by mfp.created desc) AS `last` ON last.id_subsection = mfs.id_subsection
WHERE (mfs.id_section = 11) AND (mfs.active = 'Y')
GROUP BY `mfs`.`id_subsection`

Помогите кому не лень все это разбирать, задача на самом деле интересная.

Неактивен

 

#2 12.03.2012 12:12:30

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Помогите с выборкой и ее оптимизацией

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

Неактивен

 

#3 12.03.2012 13:56:27

frm
Участник
Зарегистрирован: 27.09.2011
Сообщений: 8

Re: Помогите с выборкой и ее оптимизацией

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

Неактивен

 

#4 12.03.2012 14:00:04

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Помогите с выборкой и ее оптимизацией

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

Неактивен

 

#5 12.03.2012 14:10:05

frm
Участник
Зарегистрирован: 27.09.2011
Сообщений: 8

Re: Помогите с выборкой и ее оптимизацией

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

Отредактированно frm (12.03.2012 14:11:15)

Неактивен

 

#6 12.03.2012 14:21:57

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Помогите с выборкой и ее оптимизацией

Не присутствует, так как "при подсчете количества ответов учитывать только посты типа type='default', а при выводе последнего сообщения посты любого типа"

Кроме того, зачем вы делаете в запросе left join вместо join?

Неактивен

 

#7 12.03.2012 15:46:51

frm
Участник
Зарегистрирован: 27.09.2011
Сообщений: 8

Re: Помогите с выборкой и ее оптимизацией

На счет данных вы правы, ни все присутствуют, а left join для пустых разделов. А если без условия type='default' есть вариант решения?

Отредактированно frm (12.03.2012 15:48:09)

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson