14

У меня есть набор иерархических данных, используемых в базе данных SQL Server. Данные хранятся в качестве первичного ключа в качестве ориентира, а parentGuid - как внешний ключ, указывающий на непосредственный родительский объект. Я чаще всего получаю доступ к данным через Entity Framework в проекте WebApi. Чтобы сделать ситуацию немного более сложной, мне также необходимо управлять разрешением на основе этой иерархии, так что разрешение, применяемое к родительскому объекту, применяется ко всем его потомкам. Мой вопрос таков:Иерархические данные SQL (Рекурсивный CTE против HierarchyID против таблицы закрытия)

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

  1. Я могу создать Recursive CTEs, Common Table Expression, (aka RCTE) для обработки иерархических данных. Это, по-видимому, самый простой подход для обычного доступа, но я беспокоюсь, что он может быть медленным при использовании для определения уровней разрешений для дочерних объектов.
  2. Я могу создать поле типа данных hierarchyId в таблице и использовать предоставленные SQL Server функции, такие как GetAncestor(), IsDescendantOf() и т. Д. Это похоже на то, что запрос будет довольно простым, но, похоже, требует довольно сложного триггера вставки/обновления чтобы сохранить правильность поля hierarchyId через вставки и перемещения
  3. Я могу создать closure table, который сохранит все отношения в таблице. Я предполагаю, что это как таковой: родительский столбец и дочерний столбец, будут представлены все родительские -> дочерние отношения. (т. е. 1-> 2 2-> 3 будет представлено в базе данных как 1-2, 1-3, 2-3). Недостатком является то, что для этого требуются триггеры вставки, обновления и удаления, хотя они довольно просты, и этот метод генерирует множество записей.

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

PS Я также открыт для любых альтернативных решений этой проблемы

+0

Пожалуйста, отметьте свой вопрос версией SQL Server, которую вы используете. Ваши запросы имеют тенденцию прогрессировать от ребенка к родительскому лицу, или наоборот? RCTE, идущий вверх по дереву, следуя родительским ссылкам для одного ребенка, не должен быть слишком плохим. Идет другой путь для всех детей, где он замедляется. – HABO

+0

Я не могу проверить версию сейчас, но позже. Я думаю, что это либо 2008 год, либо новее. Вполне вероятно, что я чаще буду получать детей от родителей, чем получать родителей от детей. – jp36

+0

Я не могу добавить еще один тег, но это SQL Server 2008 r2. – jp36

ответ

9

Я использовал все три метода. Это в основном вопрос вкуса.

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

Закрывающий стол больше похож на дополнение к вышесказанному. Поиск всех потомков данного узла молниеносно, вам не нужны CTE, просто одно дополнительное соединение, так что это мило. Да, количество записей взрывается, но я думаю, что это не более чем N-1 раз число узлов для дерева глубины N (например, третичное дерево глубины 5 потребует 1 + 3 + 9 + 27 + 81 = 121 соединений при сохранении только отношения родитель-потомок против 1 + 3 + (9 * 2) + (27 * 3) + (81 * 4) = 427 для таблицы закрытия). Кроме того, записи таблицы закрытия настолько узки (как минимум, 2 интервала), что они занимают почти не место. Создание списка записей для вставки в таблицу замыкания при вставке новой записи в иерархию занимает небольшую часть служебных данных.

Мне лично нравится HierarchyId, так как он действительно сочетает в себе преимущества двух вышеупомянутых, которые представляют собой компактное хранилище и быстрый доступ молниеносно. Как только вы его настроите, легко запросить и занимает очень мало места. Как вы уже упоминали, немного сложно перемещать поддеревья, но это управляемо. В любом случае, как часто вы перемещаете поддерево в иерархии? Есть некоторые ссылки, которые вы можете найти, которые будут предлагать некоторые методы, например.:

http://sqlblogcasts.com/blogs/simons/archive/2008/03/31/SQL-Server-2008---HierarchyId---How-do-you-move-nodes-subtrees-around.aspx

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

+3

Два недостатка, с которыми я столкнулся в своем первоначальном набеге на иерархию, были, во-первых, обработку массовых вставок/обновлений в триггере insert/update и, во-вторых, использование таблицы hierarchyId'd в C# через Entity Framework. У вас есть какие-либо советы или информация об управлении ими? Кроме того, этот ответ был очень полезен, но я немного подожду, прежде чем я его подберу, чтобы побудить кого-нибудь, у кого может быть полезная информация. – jp36

+0

Существует еще один недостаток, с которым вы сталкиваетесь при попытке использовать эти отношения в Analysis Services независимо от метода, выбранного для их представления. Self-joins, loop и hierarchyid не поддерживаются в Analysis Services, что означает, что вам нужно выполнить одно из двух обходных решений; (1) создавать сглаживающие представления для представления размеров, сохраненных таким образом, или (2) использовать SSIS для выравнивания измерений в базу данных BI (схема звезд или снежинок). Либо обходное решение требует обслуживания, когда новый элемент добавляется в иерархию. – JamieSee