分层SQL数据(递归CTE与HierarchyID与闭包表)

时间:2013-06-25 16:30:11

标签: sql-server common-table-expression recursive-query hierarchyid transitive-closure-table

我在SQL Server数据库中使用了一组分层数据。数据以guid作为主键存储,而parentGuid作为指向直接父对象的外键存储。我最常通过WebApi项目中的Entity Framework访问数据。为了使情况更复杂,我还需要基于此层次结构管理权限,以便应用于父级的权限适用于其所有后代。我的问题是:

我已经搜遍过,无法决定哪种方法最适合处理这种情况。我知道我有以下选择。

  1. 我可以创建Recursive CTEs,公用表表达式(又名RCTE)来处理分层数据。这似乎是正常访问的最简单方法,但我担心在用于确定子对象的权限级别时可能会很慢。
  2. 我可以在表中创建一个hierarchyId数据类型字段,并使用SQL Server提供的函数,例如GetAncestor()IsDescendantOf()等。这似乎会使查询变得相当容易,但似乎需要一个相当复杂的插入/更新触发器,以通过插入和移动来保持hierarchyId字段正确
  3. 我可以创建一个closure table,它会存储表中的所有关系。我想象如下:父列和子列,每个父 - >将代表儿童关系。 (即1-> 2 2-> 3将在数据库中表示为1-2,1-3,2-3)。缺点是这需要插入,更新和删除触发器,即使它们非常简单,并且此方法会生成大量记录。
  4. 我尝试过全面搜索,但在这三种方法之间找不到任何建议。

    PS我也对此问题的任何替代解决方案持开放态度

1 个答案:

答案 0 :(得分:10)

我使用了所有三种方法。这主要是品味问题。

我同意表中具有父子关系的层次结构是最简单的。移动子树很简单,使用CTE编写递归访问很容易。如果您有非常大的树结构并且经常访问分层数据,那么性能只会成为一个问题。在大多数情况下,如果表上有正确的索引,递归CTE会非常快。

封闭表更像是对上述内容的补充。找到给定节点的所有后代是快速的,你不需要CTE,只需要一个额外的连接,所以它很好。是的,记录数量爆炸,但我认为它不超过深度为N树的节点数的N-1倍(例如,深度为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

我发现hierarchyId的主要缺点是学习曲线。与其他两种方法一样,如何使用它并不明显。我曾与一些非常聪明的SQL开发人员合作过,他们经常会遇到这种情况,所以你最终会遇到一两位常驻专家,他们必须向其他人提出问题。