Иерархические данные SQL (рекурсивный CTE vs HierarchyID vs таблица закрытия)
у меня есть набор иерархических данных, используемых в базе данных SQL Server. Данные хранятся с идентификатором guid в качестве первичного ключа и parentGuid в качестве внешнего ключа, указывающего на объекты непосредственного родителя. Я чаще всего получаю доступ к данным через Entity Framework в проекте WebApi. Чтобы сделать ситуацию немного сложнее, мне также нужно управлять разрешением на основе этой иерархии так, чтобы разрешение, примененное к родителю, применялось ко всем его потомкам. Мой вопрос это:
Я искал повсюду и не могу решить, что лучше справиться с этой ситуацией. Я знаю, что у меня есть следующие варианты.
- я могу создать
Recursive CTEs
, общее табличное выражение, (он же RCTE) для обработки иерархических данных. Это кажется самым простым подходом для обычного доступа, но я беспокоюсь, что он может быть медленным при использовании для определения уровней разрешений для дочерних объектов. - я могу создать
hierarchyId
поле Тип данных в таблице и используйте предоставляемые SQL Server функции, такие какGetAncestor()
,IsDescendantOf()
и т. д. Это похоже на то, что это сделает запрос довольно легким, но, похоже, требует довольно сложного триггера вставки/обновления, чтобы сохранить поле hierarchyId правильным через вставки и перемещения - я могу создать
closure table
, который будет хранить все связи в таблице. Я представляю себе это как таковое: Родительский столбец и дочерний столбец, каждая связь родитель -> ребенок будет представлена. (ie 1 - >2 2 - >3 будет представлен в базы данных как 1-2, 1-3, 2-3). Недостатком является то, что для этого требуются триггеры insert, update и delete, хотя они довольно просты, и этот метод генерирует много записей.
Я пробовал искать повсюду и не могу найти ничего, дающего какие-либо советы между этими тремя методами.
PS Я также открыт для любых альтернативных решений этой проблемы
1 ответов
Я использовал все три метода. Это в основном вопрос вкуса.
Я согласен, что иерархия с отношениями родитель-потомок в таблице является самой простой. Перемещение поддерева просто, и легко закодировать рекурсивный доступ с помощью CTEs. Производительность будет проблемой, только если у вас очень большие древовидные структуры и вы часто обращаетесь к иерархическим данным. По большей части рекурсивные CTEs очень быстры, когда у вас есть правильные индексы на таблица.
таблица закрытия больше похожа на дополнение к вышесказанному. Поиск всех потомков данного узла происходит молниеносно, вам не нужны CTEs, только одно дополнительное соединение, поэтому это сладко. Да, количество записей взрывается, но я думаю, что это не более чем N-1 раз количество узлов для дерева глубины N (например, третичное дерево глубины 5 потребует 1 + 3 + 9 + 27 + 81 = 121 соединения при сохранении только отношения родитель-потомок против 1 + 3 + (9 * 2) + (27 * 3) + (81 * 4) = 427 для таблицы закрытия). Кроме того, записи таблицы закрытия настолько узки (как минимум 2 ints), что они почти не занимают места. Создание списка записей для вставки в таблицу закрытия при вставке новой записи в иерархию требует незначительных затрат.
Мне лично нравится HierarchyId, так как он действительно сочетает в себе преимущество вышеупомянутых двух, что является компактным хранилищем и молниеносным доступом. Как только вы получите его настроить, легко и запросов занимает очень мало места. Как вы упомянули, немного сложно перемещать поддеревья, но это управляемо. В любом случае, как часто вы действительно перемещаете поддерево в иерархии? Есть некоторые ссылки, которые вы можете найти, которые предложат некоторые методы, например:
основным недостатком, который я нашел в hierarchyId, является кривая обучения. Не так очевидно, как работать с ним, как с двумя другими методами. Я работал с некоторыми очень яркими разработчиками SQL, которые часто зацеплялись за него, поэтому вы в конечном итоге получаете одного или двух экспертов-резидентов, которые должны задавать вопросы от всех остальных.