Съхраняване на дървета в база данни

Преди шест месеца написах пакет ClosureTable за рамката Laravel 3. Причината да напиша това беше тази прекрасна презентация на Бил Каруин относно начините за съхраняване и обработка на йерархични данни в MySQL с помощта на PHP.

Така. Има няколко шаблона за проектиране на бази данни за съхраняване и обработка на йерархични структури:

  • Списък на съседство("списък на съседство")
  • Материализиран път(„материализиран път“)
  • Вложени набори
  • Затваряща таблица("таблица за връзки")

Какво е таблица за затваряне

Същността на този модел на проектиране е, че връзките между обектите се съхраняват в отделна таблица, докато основната таблица съдържа само данните на самите обекти.

Таблицата с връзки трябва да съдържа поне две полета:

  • препратка към предшественик (предшественик)
  • препратка към потомък (потомък)
Нека работим върху създаването на друга SuperPuper CMS и да започнем да разработваме модул за редактиране на текстови страници. Имаме нужда от две таблици:
  • страницище съдържа данни за страници
  • pages_treepathще съдържа данни за йерархията на страниците
данни
Схема на DB таблица

Нека използваме следната йерархия на страниците като пример:

дървета

Потомствена проба

Това е SQL заявката, която ще получим, ако искаме да селектираме всички страници от секцията "За компанията".

данни
Разклонението на резултата. Стрелките показват връзки между страници

Вземане на проби от предци

А сега отдолу нагоре: нека видим всички "родители" на страницата "Корпоративна".

В този случайобратно. Търсим страници по-високо в йерархията, така че се присъединяваме към таблицата с връзки с условието, че id на страницата id трябва да бъде равен на препратката към предшественика ancestor , а изборът се прави чрез препратката към потомъка descendant, в нашия случай равен на 11 .

Вмъкване на нов елемент

Можете да добавите нова работа. Тези стойности в нашия случай не представляват, така че нека да разгледаме самата заявка.

Първата заявка е ясна - това е просто вмъкване на нови данни. Но втората заявка трябва да бъде анализирана по ред, така че нека да видим какво става тук.

заявка
Връзки между елементи след вмъкване на нова свободна позиция

Изпълнявайки тази заявка, ще получим следния списък с релации:

Нека добавим още една заявка към предишната заявка, като се присъединим към:

Премахване на елемент

И сега ще затворим свободното място за уеб дизайнер.

Изтриване на вложено дърво

Внезапно се случи така, че от известно време ABC спря да разработва уебсайтове. Трябва да изпълним следната заявка, за да премахнем съответния подраздел:

За разлика от предишната заявка, тази е малко по-сложна и първо се изтриват самите страници и едва след това връзките между тях (тъй като последните се използват активно при изтриването на първите).

Сложността на заявките отчасти се дължи на факта, че MySQL не позволява заявка да изтрива записи с клауза WHERE, която съдържа SELECT от същата таблица. В случая с MySQL сме принудени да поставим SELECT заявки във временна таблица. Най-общо нашите запитвания ще изглеждат така:

Ако погледнете по-отблизо вложената заявка SELECT в заявката DELETE от таблицата на страниците, ще откриете, че вече сме покрили подобна заявка. Този се различава от предишнияID на страницата. В резултат на селекцията получаваме всички дъщерни страници на секцията „Сайтове“ (включително самата секция) и след това изтриваме всички страници с получените идентификатори.

ниво на гнездене

Можете също да добавите поле към таблицата за връзки, което контролира нивото на влагане на елементите. Това поле ще ви позволи да правите по-прости заявки за избор на непосредствени предци или непосредствени потомци. Например:

данни
Схема на DB таблица

И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".