LINUX.ORG.RU

Помогите составить SQL-запрос для вывода дерева комментариев.

 ,


1

1

Самый простой и вместе с тем гениальный способ построения дерева, это не использовать никакие id, parent_id, не использовать никакие id_left, id_right смещения, какие там ещё виды дерева бывают...

Самый верный, ИМХО, способ, — сохранять полный путь, который проходит каждое сообщение.

Написали сообщение с ID = 1, затем на него ответили, ответ с ID = 2, а его путь, коли оно является ответом на первое сообщение, будет: «1 2».

Таблица вида

comment_id | thread_path
------------------------
         1 |           1
         2 |         1.2
         3 |         1.3

Где количеством отсутступов «вправо» в дереве является количество ответов в thread_path, и всего один SQL-запрос для вывода всего, с группировкой по дереву.

<?php $try = $dbh->query('SELECT * FROM forum GROUP BY thread_path'); ?>
<?php while ($row = $try->fetchArray()): extract($row); unset($row); ?>
<?php echo str_repeat('&nbsp; ', substr_count($thread_path, '.')); ?>
<?php echo $comment_text; ?>
<?php endwhile; ?>

Рабочий пример: http://spfng.com/forum/

Теперь вопрос. Я не знаю как сделать, чтобы выводить сообщения в обратном порядке, но при этом целиком сохранялось дерево ответов.

Подскажите? Как сделать, чтобы все корневые сообщения, у которых thread_path = comment_id, выводились в обратном порядке, ORDER BY comment_id DESC, но при этом все другие сообщения, которые являются ответами на корневые (которые начинаются как thread_path LIKE comment_id || ".%"), не меняли свой порядок вывода, а оставались как есть сейчас, ORDER BY comment_id ASC. Как-то так...

Это реально в один SQL-запрос уместить?

★★★★★

Самый простой и вместе с тем гениальный способ построения дерева, это не использовать никакие id, parent_id, не использовать

Ну-ну, далеко пойдешь. Если бы ты строил дерево как все нормальные люди с id и parent_id, то мог бы потом по-царски написать рекурсивный запрос, и тогда у тебя что угодно влезло бы в этот один запрос. Например так:

with threads_recursive (comment_id, comment_data) as (
  select i.comment_id, 
         i.comment_data 
  from comments i 
  where i.comment_id = 1
  union all
  select c.comment_id, 
         c.comment_data 
  from threads_recursive p 
  inner join comments c on c.parent_id = p.comment_id
) select * from threads_recursive;

Но ты закладываешь весьма странное решение в свою конструкцию, и поэтому тебе предстоит всю дорогу трахаться с substr(instr(substr(instr())))). Причем, не факт, что у тебя всегда получится выражать то, что там тебе понадобится.

alex_the_v ★★★
()
Последнее исправление: alex_the_v (всего исправлений: 1)

Не проверял

select * from forum f 
left join (select substring_index(thread_path, '.', 1) as root,
             max(comment_id) as max 
             from forum group by root
          ) o on substring_index(f.thread_path, '.', 1)=o.root 
order by o.max desc, f.thread_path

По идее лучше добавить дополнительное поле, запрос тормозной.

goingUp ★★★★★
()
Последнее исправление: goingUp (всего исправлений: 1)

Самый верный, ИМХО, способ

Есть еще nested set

goingUp ★★★★★
()

Добавь столбец root_comment_id и делай order by root_comment_id desc, thread_path.

Legioner ★★★★★
()
Ответ на: комментарий от alex_the_v

Но ты закладываешь весьма странное решение в свою конструкцию

Это типовое решение. Самое быстрое, когда требуется выборка по конкретному пути. С parent_id придётся крутить циклы.

Другое дело, что обычно в таком случае не экономят и хранят как «собранный» путь, так и parent_id :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

хранят как «собранный» путь, так и parent_id

Разумная тема, кстати. Spoofing, ты слышишь?

cамое быстрое, когда требуется выборка по конкретному пути

Рекурсивный SQL все равно будет быстрее и удобнее в большинстве задач. Путь собрать - тоже невелика проблема:

with threads_recursive (comment_id, comment_data, comment_path) as (
  select i.comment_id, 
         i.comment_data 
         i.comment_id as comment_path
  from comments i 
  where i.comment_id = 1
  union all
  select c.comment_id, 
         c.comment_data 
         p.comment_path || c.comment_id as comment_path
  from threads_recursive p 
  inner join comments c on c.parent_id = p.comment_id
) select * from threads_recursive;

А вот если тебе наоборот надо будет достать айдишники из готового пути в VARCHAR, то придется поизвращаться.

А вообще, если ты уж пишешь веб-приложение на пыхе, то надо пользоваться ORM и не морочить себе голову.

alex_the_v ★★★
()
Последнее исправление: alex_the_v (всего исправлений: 2)
Ответ на: комментарий от alex_the_v

Иначе говоря, при наличии ОРМа сиквель можно не учить? Последнее время я примерно так и представляю назначение этой надстройки — дать возможность работы с базой для неосиляторов, которая тщательно маскируется тезисом про независимость от СУБД. На практике при этом СУБД почти в 100 процентах случает никогда не меняется.

Надеюсь я не прав.

Jaberwock ★★★
()

По теме — забить и строить дерево своим пыхом или шаблонизатором на фронтэнде.

Deleted
()

Нафига тебе полный thread_path, храни root_path, это гораздо юзабельнее. Если без полного пути никак - храни оба.

ya-betmen ★★★★★
()
Ответ на: комментарий от Jaberwock

Иначе говоря, при наличии ОРМа сиквель можно не учить?

Ну, не хочешь учить - не учи, будь лохом. Только не приписывай эти слова мне, я такого не говорил.

Надеюсь я не прав

Ты не прав. ORM призван закрыть разрыв между реляционной и объектной парадигмами путем представления реляционных сущностей в виде нативных объектов. Он не обязательно будет db-agnostic, и это не является его основной целью.

alex_the_v ★★★
()

Самый простой и вместе с тем гениальный способ построения дерева

Ну-ну. Кнута почитай сначала, прежде чем писать «гениальный».

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.