Задача такая же как и в предыдущей статье, только применимо к MySQL.
Грабли
Хорошая новость ребята! В MySQL нет проблемы с рекурсивными триггерами! Разработчики MySQL просто тупо лочат изменяемую таблицу даже на уровне триггера, вот редиски. Но, собственно, нас может остановить только отключение электричества.
Есть небольшая лазейка, с... объединенными таблицами. Хотя я не нашел в документации подтверждения того, что это так специально было задумано, но и отрицания тоже не было. Правда есть вероятность того, что эту лазейку могут прикрыть, хотя я не вижу в этом смысла.
Увы, механизм триггеров в MySQL новый и довольно сырой, что накладывает некоторые ограничения на его использование, но все же его достаточно для решения нашей задачи.
Итак, исходная таблица:
На основе нее делаем точно такую же таблицу, с точно таким же набором полей:
Ключевое слово - MERGE по сути мы создаем представление нашей таблицы, с которым мы можем работать как с таблицей. Вообще механизм MERGE тоже немного сыроват. Структура объединенной таблицы должна быть точно такой же как исходной.
ВАЖНО!!! При изменении структуры исходной таблицы связь между таблицами - теряется, а вот между уже связанными строками - НЕТ! Будьте внимательны. При изменении структуры исходной таблицы обязательно это же изменение применить и к объединенной!
Так же исходная таблица должна быть MyISAM, оно и понятно, транзакции в этом случае не применимы ввиду того, что таблицы не могут друг друга лочить, а данные в них одни и те же. Впрочем, для нас это не страшно, так как в пределах триггера исходная таблица будет залочена, а изменение данных объединенной таблицы мы будем производить из триггеров, поэтому пересечений запросов быть не может.
Еще, на что хотел обратить внимание: Настоятельно рекомендую не использовать корневые узлы в пределах одного дерева (о чем я говорил в предыдущей статье), так как таблица блокируется полностью, и может формироваться слишком длинная очередь.
Создание записи
MySQL диалект триггеров несколько отличается от диалекта PostgreSQL:
Не обязательно объявлять переменные в начале процедуры;
Прервать выполнение триггера нельзя, он должен отработать полностью;
Как было сказано выше, рекурсии мы не боимся, поэтому лишние проверки и дополнительные поля нам не потребуются;
Поэтому несколько изменяется логика:
Переменные появляются по ходу кода;
Вместо возвратов из триггера, оборачиваем код условиями;
Все изменения, касающиеся структуры дерева, применяем к вспомогательной объединенной таблице;
Изменение записи
Принцип тот же с применением диалекта.
ВНИМАНИЕ!!! В MySQL имеет значение порядок перечисления полей в запросе UPDATE, так как поля изменяемые во время запроса в самом же запросе меняют значение на новое, поэтому, если мы дальше по запросу используем эти поля в условиях, результат будет неадекватным.
Удаление записи
Из-за отсутствия проблем с рекурсией триггеров, удаление вообще становится тривиальной задачей.
Триггер для варианта: "удаление ветки целиком":
Триггер для варианта: "удаление узла со смещением дочерних узлов на уровень ввверх":
Хочу обратить внимание на то, что изменения затрагивающие структуру дерева нужно производить не пачками, а последовательно для каждого узла, что бы сохранялась его целостность.
Собственно все. Осталось только проставить индексы (мне опять же лениво сюда писать SQL команды, поэтому просто их озвучу):
Композитный не уникальный на поля (left_key, right_key, level, tree);