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

jp3*_*p36 15 sql-server recursive-query common-table-expression hierarchyid transitive-closure-table

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

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

  1. 我可以创建Recursive CTEsCommon Table Expression(也就是RCTE)来处理分层数据.这似乎是正常访问最简单的方法,但我担心在用于确定子对象的权限级别时可能会很慢.
  2. 我可以创建一个hierarchyId表中,并使用SQL Server所提供的功能,如数据类型字段GetAncestor(),IsDescendantOf()和等,这似乎将让查询很容易,但似乎需要一个相当复杂的插入/更新触发器,以保持HIERARCHYID域是正确的通过插入和移动
  3. 我可以创建一个closure table,它将存储表中的所有关系.我想象如下:父列和子列,每个父 - >子关系将被表示.(即1-> 2 2-> 3将在数据库中表示为1-2,1-3,2-3).缺点是这需要插入,更新和删除触发器,即使它们非常简单,并且此方法会生成大量记录.

我已经尝试过全面搜索,但在这三种方法之间找不到任何建议.

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

Kev*_*cki 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开发人员合作过,他们经常会遇到困难,所以你最终会遇到一两位常驻专家,他们必须向其他人提出问题.

  • 我在初次尝试进入hierarchyId时遇到的两个缺点是,首先,在插入/更新触发器中处理批量插入/更新,其次,在C#到Entity Framework中使用hierarchyId'd表.您对处理其中任何一个有什么建议或信息吗?此外,这个答案非常有用,但在我选择之前我会稍等一下,以鼓励其他可能提供有用信息的人. (3认同)
  • @jp36 `HierarchyId` 现在是 `EF Core 8.0.0-preview2` 的已解决问题。今年晚些时候发布 8.0.0 时,它将得到 EF Core 团队的支持。/sf/answers/4311444721/ (3认同)