Управление иерархиями в SQL: MPTT / вложенные наборы vs списки смежности vs пути хранения

некоторое время я боролся с тем, как лучше всего обрабатывать иерархии в SQL. Разочарованный ограничениями списков смежности и сложностью MPTT/вложенных наборов, я начал думать о простом хранении ключевых путей вместо этого, как простой node_key/node_key/... строку. Я решил скомпилировать плюсы и минусы трех методов:

количество вызовов, необходимых для создания/удаления/перемещения узла:

  • смежности = 1
  • MPTT = 3
  • Путь = 1 (Замените старый путь узла новым путем узла на всех узлах, содержащих этот путь)

количество вызовов, необходимых для получения дерева:

  • смежность = [количество подуровней]
  • MPTT = 1
  • Путь = 1

количество вызовов, необходимых для получения пути к узлу / родословной:

  • смежность = [количество супер-уровней]
  • MPTT = 1
  • путь = 0

количество звонки, необходимые для получения количества подузлов:

  • смежность = [количество подуровней]
  • MPTT = 0 (можно рассчитать по значениям справа/слева)
  • Путь = 1

количество вызовов, необходимых для получения глубины узла:

  • смежность = [количество супер-уровней]
  • MPTT = 1
  • путь = 0

обязательные поля DB:

  • смежности = 1 (родитель)
  • MPTT = 3 (Родительский, правый, левый)
  • Путь = 1 (путь)

вывод

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

Итак, вопрос в том, не следует ли хранить пути считать более сильной техникой, чем MPTT? Почему хранятся пути не более часто используемый метод, и почему бы вам не использовать их над MPTT в данном случае?

кроме того, если вы думаете, что этот анализ является неполным, пожалуйста, дайте мне знать.

обновление:

вот, по крайней мере, 2 вещи MPTT может сделать из коробки, что сохраненное решение пути не будет:

  1. позволяет вычислять количество подузлов для каждого узла без каких-либо дополнительных запросов (упомянутых выше).
  2. налагает порядок на узлы на a данный уровень. Другие решения неупорядочены.

2 ответов


вы также можете рассмотреть дизайн таблицы закрытия, который я описываю в своем ответе на каков наиболее эффективный / элегантный способ разбора плоской таблицы в дерево?

вызовы, необходимые для создания/удаления/перемещения узла:

  • закрытие = 1

вызовы, необходимые для получения дерева:

  • закрытие = 1

вызовы, необходимые для получения пути к узлу / родословной:

  • закрытие = 1

вызовы, необходимые для получения количества подузлов:

  • закрытие = 1

вызовы, необходимые для получения глубины узла:

  • закрытие = 1

требуются поля DB:

  • Adjancency = 1 больше поля / строки
  • путь = еще 1 поле / строка
  • MPTT = 2 или 3 больше полей / строк
  • закрытие = 2 или 3 поля в дополнительной таблице. Эта таблица имеет o (n^2) строк худший дело но гораздо меньше, чем в большинстве практических случаев.

есть еще несколько соображений:

поддерживает неограниченную глубину:

  • смежности = да
  • MPTT = да
  • путь = нет
  • закрытие = да

поддерживает ссылочную целостность:

  • смежности = да
  • MPTT = нет
  • путь = нет
  • закрытие = да

Я также покрываю таблицу закрытия в моей презентации модели для иерархических данных с SQL и PHP, и моей книги SQL Antipatterns: избегая подводных камней программирования баз данных.


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

уменьшая валидность метода до" количества вызовов", вы эффективно игнорируете все проблемы, которые пытаются решить хорошо понятые структуры данных и алгоритмы; то есть быстрое выполнение и низкая память и печать ресурсов.

" количество вызовов " на SQL server может показаться хорошей метрикой для использования ("look ma less code"), но если результатом является программа, которая никогда не заканчивается, работает медленно или занимает много места, на самом деле это бесполезная метрика.

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

Это может быть трудно увидеть с небольшими наборами дат (и во многих случаях небольших деревьев список лучше), попробуйте некоторые примеры на наборах данных размером 500, 1000, 10k -- вы будете быстро поймите, почему хранить весь путь-не очень хорошая идея.