Хорошие обзоры
Вообще говоря, вы принимаете решение между временем быстрого чтения (например, вложенным множеством) или временем быстрой записи (список смежности). Обычно вы получаете комбинацию из приведенных ниже вариантов, которые лучше всего соответствуют вашим потребностям. Следующее обеспечивает некоторое углубленное чтение:
- Еще одно сравнение вложенных интервалов и списка смежности : лучшее сравнение списка смежности, материализованного пути, вложенного набора и вложенного интервала, которое я нашел.
- Модели для иерархических данных : слайды с хорошими объяснениями компромиссов и примера использования
- Представление иерархий в MySQL : очень хороший обзор Nested Set, в частности
- Иерархические данные в РСУБД : наиболее полный и хорошо организованный набор ссылок, которые я когда-либо видел, но не так много для объяснения
Опции
Я знаю и общие черты:
- Список смежности :
- Столбцы: ID, ParentID
- Легко реализовать.
- Дешевый узел перемещается, вставляется и удаляется.
- Дорого, чтобы найти уровень, происхождение и потомков, путь
- Избегайте N + 1 с помощью общих табличных выражений в базах данных, которые их поддерживают
- Вложенный набор (он же модифицированный обход дерева предзаказа )
- Столбцы: слева, справа
- Дешевое происхождение, потомки
- Очень дорогие
O(n/2)
перемещения, вставки, удаления из-за нестабильной кодировки
- Таблица мостов (также называемая таблицами закрытия / триггерами )
- Использует отдельную таблицу соединений с: предком, потомком, глубиной (необязательно)
- Дешевое происхождение и потомки
- Записывает затраты
O(log n)
(размер поддерева) для вставки, обновления, удаления - Нормализованное кодирование: хорошо для статистики СУБД и планировщика запросов в соединениях
- Требуется несколько строк на узел
- Столбец Lineage (он же Материализованный путь , Перечисление пути)
- Колонка: происхождение (например, / parent / child / внук / etc ...)
- Дешевые потомки с помощью префиксного запроса (например
LEFT(lineage, #) = '/enumerated/path'
) - Записывает затраты
O(log n)
(размер поддерева) для вставки, обновления, удаления - Нереляционный: использует тип данных Array или сериализованный формат строки
- Вложенные интервалы
- Как и вложенный набор, но с реальным / float / decimal, чтобы кодирование не было изменчивым (недорогое перемещение / вставка / удаление)
- Имеет проблемы с реальным / плавающим / десятичным представлением / точностью
- Вариант матричного кодирования добавляет кодирование предка (материализованный путь) для «свободного», но с добавленной хитростью линейной алгебры.
- Плоский стол
- Модифицированный список смежности, который добавляет столбец уровня и ранга (например, упорядочение) к каждой записи.
- Дешево перебирать / разбивать на страницы
- Дорого переместить и удалить
- Полезное использование: обсуждение темы - форумы / комментарии блога
- Несколько столбцов линии
- Столбцы: по одному для каждого уровня происхождения, относятся ко всем родителям вплоть до корня, уровни ниже уровня элемента установлены в NULL.
- Дешевые предки, потомки, уровень
- Дешевая вставка, удаление, перемещение листьев
- Дорогая вставка, удаление, перемещение внутренних узлов
- Жесткий предел того, насколько глубокой может быть иерархия
Примечания к базе данных
MySQL
оракул
- Используйте CONNECT BY для прохождения списков смежности
PostgreSQL
- Тип данных дерева для Материализованного Пути
SQL Server
- Общее резюме
- 2008 предлагает тип данных HierarchyId , который помогает с подходом Lineage Column и расширяет глубину, которую можно представить.
sql
database
tree
relational-database
hierarchical-data
orangepips
источник
источник
Closure Tables
превосходятAdjacency List
,Path Enumeration
иNested Sets
с точки зрения простоты использования (и я предполагаю , что производительность, а).Ответы:
Мой любимый ответ - то, что предложено в первом предложении в этой теме. Используйте список смежности для поддержки иерархии и используйте вложенные наборы для запроса иерархии.
До сих пор проблема заключалась в том, что метод покрытия из списка смежности для вложенных наборов был ужасно медленным, потому что большинство людей используют экстремальный метод RBAR, известный как «Push Stack», для выполнения преобразования и считался дорогостоящим. достичь Нирваны простоты обслуживания с помощью Списка Смежности и потрясающей производительности Вложенных Наборов. В результате большинству людей приходится довольствоваться тем или иным, особенно если их число превышает 100 000, скажем, паршивых узлов. Использование метода push-стека может занять целый день, чтобы выполнить преобразование того, что MLM-пользователи считают малой иерархией узлов.
Я подумал, что смогу дать Селко немного конкуренции, придумав метод для преобразования Списка смежности в Вложенные множества на скоростях, которые просто кажутся невозможными. Вот производительность метода push-стека на моем ноутбуке i5.
А вот продолжительность нового метода (с методом push-стека в скобках).
Да, это правильно. 1 миллион узлов преобразуется менее чем за минуту, а 100 000 узлов - менее чем за 4 секунды.
Вы можете прочитать о новом методе и получить копию кода по следующему URL. http://www.sqlservercentral.com/articles/Hierarchy/94040/
Я также разработал «предварительно агрегированную» иерархию с использованием аналогичных методов. MLM'еры и люди, делающие списки материалов, будут особенно заинтересованы в этой статье. http://www.sqlservercentral.com/articles/T-SQL/94570/
Если вы загляните в одну из статей, перейдите по ссылке «Присоединиться к обсуждению» и дайте мне знать, что вы думаете.
источник
Это очень частичный ответ на ваш вопрос, но я надеюсь, что все еще полезно.
Microsoft SQL Server 2008 реализует две функции, которые чрезвычайно полезны для управления иерархическими данными:
Посмотрите "Моделирование ваших иерархий данных с SQL Server 2008" Кента Тегелса на MSDN для начала. См. Также мой собственный вопрос: рекурсивный запрос из одной таблицы в SQL Server 2008
источник
Этот дизайн еще не был упомянут:
Несколько столбцов линии
Хотя у него есть ограничения, если вы можете выдержать их, это очень просто и очень эффективно. Особенности:
Ниже приведен пример - таксономическое древо птиц, поэтому иерархия - Класс / Порядок / Семейство / Род / Вид - вид - самый низкий уровень, 1 строка = 1 таксон (что соответствует видам в случае узлов листа):
и пример данных:
Это здорово, потому что таким образом вы выполняете все необходимые операции очень простым способом, пока внутренние категории не меняют свой уровень в дереве.
источник
Модель смежности + модель вложенных множеств
Я пошел на это, потому что я мог легко вставлять новые элементы в дерево (вам просто нужен идентификатор ветви, чтобы вставить в него новый элемент), а также запрашивать его довольно быстро.
parent
столбец.lft
междуlft
иrgt
родитель.lft
меньшие значения, чем узлы,lft
иrgt
больше, чем узлы,rgt
и сортируете поparent
.Мне нужно было сделать доступ к дереву и запросы к нему быстрее, чем вставки, поэтому я выбрал это
Единственная проблема заключается в фиксации
left
иright
столбцов при вставке новых элементов. ну, я создал для него хранимую процедуру и вызывал ее каждый раз, когда вставлял новый элемент, что в моем случае было редкостью, но действительно быстро. Я получил идею из книги Джо Селко, и хранимая процедура и то, как я пришел к ней, объяснены здесь в DBA SE https://dba.stackexchange.com/q/89051/41481источник
children
иdescendants
.left
иright
используются для поиска потомков.Если ваша база данных поддерживает массивы, вы также можете реализовать столбец происхождения или материализованный путь в виде массива родительских идентификаторов.
В частности, в Postgres вы можете использовать операторы множеств для запроса иерархии и получить отличную производительность с индексами GIN. Это делает поиск родителей, детей и глубины довольно тривиальным в одном запросе. Обновления также довольно управляемы.
У меня есть полное описание использования массивов для материализованных путей, если вам интересно.
источник
Это действительно квадратный колышек, вопрос круглой дыры.
Если реляционные базы данных и SQL являются единственным молотком, который у вас есть или вы хотите использовать, то ответы, опубликованные до сих пор, являются адекватными. Однако почему бы не использовать инструмент, предназначенный для обработки иерархических данных? Граф базы данных идеально подходит для сложных иерархических данных.
Неэффективность реляционной модели наряду со сложностями любого решения кода / запроса для отображения графа / иерархической модели на реляционную модель просто не стоит усилий по сравнению с легкостью, с которой решение базы данных графов может решить ту же проблему.
Рассмотрим спецификацию как общую иерархическую структуру данных.
Кратчайший путь между двумя узлами : Простой алгоритм обхода графа. Приемлемые пути могут быть определены на основе критериев.
сходство : Какова степень сходства между двумя сборками? Выполните обход обоих поддеревьев, вычисляя пересечение и объединение двух поддеревьев. Процент подобный - это пересечение, деленное на союз.
Транзитивное закрытие : пройдитесь по поддереву и суммируйте интересующее вас поле (поля), например, «Сколько алюминия содержится в подсборке?»
Да, вы можете решить проблему с SQL и реляционной базой данных. Тем не менее, есть гораздо лучшие подходы, если вы хотите использовать правильный инструмент для работы.
источник
Я использую PostgreSQL с таблицами закрытия для своих иерархий. У меня есть одна универсальная хранимая процедура для всей базы данных:
Затем для каждой таблицы, где у меня есть иерархия, я создаю триггер
Для заполнения таблицы закрытия из существующей иерархии я использую эту хранимую процедуру:
Таблицы закрытия определяются 3 столбцами - ANCESTOR_ID, DESCENDANT_ID, DEPTH. Можно (и я даже советую) хранить записи с одинаковым значением для ANCESTOR и DESCENDANT и нулевым значением для DEPTH. Это упростит запросы для поиска иерархии. И они действительно очень просты:
источник