Рекурсивное само присоединение

15

У меня есть commentsтаблица, которая может быть упрощена до этого:

comments
=======
id
user_id
text
parent_id

где parent_idобнуляется, но может быть ключом для родительского комментария.


Теперь, как я могу selectвсе потомки конкретного комментария?
Комментарии могут быть на несколько уровней ниже ...

мистифицировать
источник

Ответы:

16

Иерархические запросы , поскольку эти рекурсивные запросы известны, не поддерживаются для MySQL.

Однако они поддерживаются в Oracle, Microsoft SQL Server, DB2 и PostgreSQL, среди других.

Если вам нужен обходной путь, вы можете найти динамический (и, следовательно, потенциально опасный) трюк здесь: /programming/8104187/mysql-hierarchical-queries

Вы также можете найти обсуждение того, как хранить иерархические данные в других моделях, кроме списка смежности (то есть столбца « Родитель» ), здесь: /programming/192220/what-is-the-most-efficient- элегантный способ-к-синтаксического анализа-а-плоским столик-в-а-дерево /

Удачи!

Valmoer
источник
Интересно, как это решение в вашей второй ссылке может быть опасным. Не могли бы вы объяснить это? В противном случае, добро пожаловать на сайт!
Дезсо
3
@dezso: Quassnoi, - говорит сам разработчик запроса: « * Это небезопасно для обновления *, поскольку MySQL не может четко определить поведение переменных сеанса. Однако это единственный способ своевременно обрабатывать списки смежности в запросе ». Слово «Опасный» могло быть слишком сильным ( потенциально нестабильным ?), Но я предпочитаю ошибаться из-за осторожности (плюс, я лучше разбираюсь в Oracle, чем в MySQL, поэтому я хотел быть более осторожным). Спасибо за прием, кстати! Я долгое время скрывался в сети SE и решил, что пришло время немного окупиться.
Valmoer
2
Синтаксис WITH [RECURSIVE] теперь поддерживается в mysql 8.0. dev.mysql.com/doc/refman/8.0/ru/with.html
ClearCrescendo
6

Этот дизайн таблицы представляет собой антипаттерн SQL «Наивные деревья», описанный Биллом Карвином (начиная со слайда 48 в своей презентации « Ответный удар антипаттернов SQL» ). Проблема с этим дизайном, в частности, заключается в трудности получения всех потомков (или родителей) узла. Поскольку вы используете MySQL, вы не можете использовать общие табличные выражения (оператор WITH и его модификатор RECURSIVE), присутствующие в других RDBMS.

То, что вы остались с:

  • использовать альтернативную реализацию иерархической структуры данных (ответы на этот вопрос могут быть хорошей ссылкой на это)
  • строить запросы на самостоятельное объединение с ограничением по глубине. Для глубины = 5 вы можете использовать что-то в строках:

    SELECT *
    FROM comments AS c1
      JOIN comments AS c2 ON (c2.parent_id = c1.id)
      JOIN comments AS c3 ON (c3.parent_id = c2.id)
      JOIN comments AS c4 ON (c4.parent_id = c3.id)
      JOIN comments AS c5 ON (c5.parent_id = c4.id)
  • использовать СУБД, которая поддерживает WITH RECURSIVE (хотя, скорее всего, это не вариант для большинства людей)

redguy
источник
2
Я не согласен с Биллом Карвином здесь. Модель смежности не является анти-паттерном. В современной СУБД, которая поддерживает рекурсивные запросы (Oracle поддерживает это более 20 лет), такая модель очень эффективна для поиска и обновления.
a_horse_with_no_name
5

MySQL не поддерживает рекурсивные запросы, такие как тот, который вам нужен.

Некоторое время назад я написал «Хранимые процедуры», которые обеспечивают модель для этого.

Вместо того, чтобы изобретать велосипед, я дам вам ссылки на мои прошлые сообщения на эту тему:

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

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

Родитель всем детям (например, хранимая процедура GetFamilyTree)

  • STEP01) Начните с parent_idочереди
  • STEP02) вывести следующий из очереди parent_idкак текущий
  • ШАГ03) Поставить в очередь все idзначения, которые имеют текущийparent_id
  • STEP04) Распечатать или собрать комментарий
  • STEP05) Если очередь не пуста, перейдите STEP02
  • Шаг 06) Вы сделали !!!

Родитель для всех родителей (как хранимая процедура GetAncestry)

  • STEP01) Начните с idочереди
  • STEP02) вывести следующий из очереди idкак текущий
  • ШАГ03) Поставить parent_idзначение текущегоid
  • STEP04) Распечатать или собрать комментарий
  • STEP05) Если очередь не пуста, перейдите STEP02
  • Шаг 06) Вы сделали !!!

Пожалуйста, просмотрите Хранимые процедуры в моих других постах, чтобы увидеть реализацию.

Попробуйте!

RolandoMySQLDBA
источник
2
SELECT  group_concat(@id :=
        (
        SELECT  id
        FROM    comments
        WHERE   parent_id = @id
        )) AS comment
FROM    (
        SELECT  @id := 1
        ) vars
STRAIGHT_JOIN
        comments
WHERE   @id IS NOT NULL

играть на скрипке

Правин Прасаннан
источник