以 EF 核心和 PostgreSQL @(}Overview) 管理等级数据
一. 导言导言 导言 导言 导言
在软件开发中,等级数据无处不在 :}(线性评论),}组织图表 *,}文件系统 <,}产品类别,}♪和论坛讨论 有关这个话题的首篇文章,以及基本挑战依然如故,
为何SQL的等级结构很困难
基本问题在于: 相关数据库在数组中思考@, @% 而不是树Joe Celko在他的杰出著作中解释, 以一组思考,SQL在整张表格上运行 ,}不是单行=.
当您写入 SQL 查询\ ,}数据库引擎运行时 一组行100"或"join客户购买的订单
但等级制度是内在的 连续循环找到一个节点的所有子孙 您需要到 :
- 寻找直系子女
- 每个孩子都能找到 他们的 儿童子女
- 重复一遍,直到你踏遍了整个树下
此递归的十字路由 @ ' @ t映射为自然设置操作@ MS K1} 您可以在任何深度表达 @ I"} 将我所有的子孙都递解成一个单一的\ ,简单的 SQL 语句, 其中不包含 @ MPK6}
- 递续 CTE (+SQL:1999,+但计算成本昂贵
- 多次往返访问数据库
- 预先计算关系的聪明的去正常化
在本系列中,每种方法都代表了一种不同的交易 -Q 。 在复杂的写法\ ,Q 读起复杂的事情\ MS K2QQ 和存储的间接费用\ MSSK3Q 之间有一个不同的交易。 没有免费的午餐\ I-Q你='ZARE总是以一个价格换另一个#.}请见Joe Celko'Qs. SQL 中用于智能的树和层次深入地涵盖所有这些方针的,*}.
示例:串列注释
要比较混凝土@, @ we'}将使用线性评论作为我们运行中的示例 @-这个博客就使用了。 批注线索可能看起来像此:}
flowchart TD
subgraph Post["Post: How to Deploy Docker Containers"]
C1["Comment 1: Great article!<br/>depth 0"]
C2["Comment 2: Thanks!<br/>depth 1"]
C3["Comment 3: Very helpful indeed<br/>depth 1"]
C4["Comment 4: Agreed!<br/>depth 2"]
C5["Comment 5: What about Kubernetes?<br/>depth 0"]
C6["Comment 6: That's covered in part 2<br/>depth 1"]
end
C1 --> C2
C1 --> C3
C3 --> C4
C5 --> C6
style Post stroke:#10b981,stroke-width:2px
style C1 stroke:#6366f1,stroke-width:2px
style C2 stroke:#8b5cf6,stroke-width:2px
style C3 stroke:#8b5cf6,stroke-width:2px
style C4 stroke:#a855f7,stroke-width:2px
style C5 stroke:#6366f1,stroke-width:2px
style C6 stroke:#8b5cf6,stroke-width:2px
我们需要支持这些行动
- 获取所有文章的备注 @(#有线条结构保存 #)#
- 把所有祖先都带走 @(breadcrumb track)}评论中的评论
- 把所有后代都带走 @(_entire subtree@)}评论中的评论
- 添加新注释 @( 作为对现有评论的回复@)
- 删除子树 @(remove 一项评论和所有答复@)
- 移动子树 @(@ recrown a 批注 *%s K1}稀有的,}但有时需要
五方针
每种方针都在其文章中详细论述,
-=YTET -伊甸园字幕组=- 翻译:
最简单和最直观的方法.每个行商店都引用其父节点@."It'" 大多数开发者首先达到什么目的, 因为它自然地映射到我们对等级的思考方式.
如何运作? 每条评论均无效 ParentCommentId “.” 列中根注有 NULL=,}回答指向他们的父@.}
最好的为: 当您想要 EF Core@' @%s 导航属性自然工作时, shall 级别为 @ 5-6 @ level@ MS K2 @ 经常在子树下移动\ , @
**交易 ** 要获得祖先或后代,需要循环性幼儿保育或多个查询@. 简单写 @, 读深树可能较慢@MS K2
* 2. 关闭表
预估并存储所有祖先的子孙关系, 而不是在查询时间找到对方的关系@,}我们直接保存他们的深度 @.}
如何运作? A 单独一 CommentClosure 在“1”评论下, Comment@3将包含以下条目: @:+Q(1,4,2), @ @#(3,4,1),_Z(4,4,0).}
最好的为: 当您在任意深处需要查询时, 当您很少移动子树@ . k4\ 时, read -\ hiarvy applications\ MS K1\
**交易 ** 更复杂的插入@ ( @% 必须添加关闭条目@ I),} 储存随着深度的增加而增长 *\ MS K2# 移动子树需要重建关闭@ MPK3}
3. 定義路徑
将完整的祖先储存为定界的字符串 @. 把它视为存储完整邮政地址,而不仅仅是街道名@.
如何运作? 每条评论都有: Path 如 {"1/3/7"} 列, 意思是"} root is @1,} 父系是 这是7".} 祖先可以从字符串@;} 子孫中解析 。
最好的为: 当对祖先的询问比后代多时... ..."," 当你想要人类的时候..."-"可读调试路径 . t
**交易 ** 查询要慢一点, 没有正确的索引@ , @ 路径字符串有长度限制 @ MS K1 @ 移动子树需要更新所有后人路径@ I. @
4.+Nasted 套件
从深度中指定每个左节点和右边边界号@ - @ 第一次跨度 @ MS K1}所有后代都有价值 之间 父+'%s 边框\ .}
如何运作? 每项评论的评论 Left 和 Right 值Q. 与 L=4, RMS K2 的節点包含所有节点, 其中在 & 4 @\ < 左侧和右侧@< @ @ @\7. 后代端点中找到简单的范围查询@MS K7}
最好的为: 读取@ - @ hheavy,} 写下 @ MS K2 @ rarely 假想類似類型樹的區域@ I.} 非常好, @ MPK4} 以顯示的順序將整個子串按下方的排列 * " question\ .}
**交易 ** 插入需要更新很多行@( translate all value 以便让房间 @),}移动子树复杂 *. 不适合经常写入_.}
5. PostgreSQL 一棵树
PostgreSQL'%s 本地的等级数据扩展@. 类似已实现路径,但有数据库{-} 级别优化和强势模式匹配.
如何运作? 用途和用途 ltree 具有像 @"1.3.7". GiST 索引等路径的数据类型,使使用操作员这样的子民查询能够实现高效的祖先{/} @> 和 <@.
最好的为: PostgreSQL-}只允许在性能很重要的地方部署@ ,}当您需要模式匹配查询时 @ MS K2} 当您想要最佳的已实现路径\ .}
**交易 ** PostgreSQL @ MSKK0 @ 仅@ just\ MS K1} 添加数据库扩展依赖性@ . notte @ MPK3 Npgsql 服务提供商支持 LINQ 翻译 树的果实, LTree 类型@, @}虽然循环 CTE仍然需要原始 SQL.}
比较对比摘要
| Q 移动子树 MS K5Q 存储 @ | EF核心支持 # | # | ||||
|---|---|---|---|---|---|---|
| 相邻名单 和CTE | OO MS K6D)Q 与CTE | O NSK9 | ||||
| 审结表 @ | @ O(d) @ @ @ I | O#(1) @ ( | OMS K6) @ NSK7 O}(s x d□)* * MSk9QQQZOO(nxd) | 好? | ||
| 定文件路径 MSKOOO O(1) NSK2 O(1)* | OO(1)QO MS K2ZO I(AS)Q | OMSOK6QD每个節点 MSKO8}好的 | ||||
| 括号集 @ | @ OQ(n) @ @ I | OO(1) @ ( | OQ(1)) MS K7 OZ(N}) @ | Minimal@ | 好□ | # |
| 树叶 O | OO) | O(D)+每个节点 @ | +好~( LTree 类型@) @ @% |
@% |
n= 總結節點MS K1 d= 深度 , s @= 子樹的大小 *使用正确的索引
决策流程图
flowchart TD
Start([Start]) --> Q1{Shallow tree?<br/>< 5 levels}
Q1 -->|Yes| Q2{Need EF Core<br/>navigation properties?}
Q2 -->|Yes| AL[Adjacency List]
Q2 -->|No| Q3{Performance<br/>critical?}
Q3 -->|No| AL
Q3 -->|Yes| MP[Materialised Path]
Q1 -->|No| Q4{Read-heavy?}
Q4 -->|Yes| Q5{Writes rare?}
Q5 -->|Yes| NS[Nested Sets]
Q5 -->|No| CT[Closure Table]
Q4 -->|No| Q6{PostgreSQL only?}
Q6 -->|Yes| LT[ltree]
Q6 -->|No| Q7{Need breadcrumbs?}
Q7 -->|Yes| MP
Q7 -->|No| CT
style Start stroke:#10b981,stroke-width:2px
style AL stroke:#6366f1,stroke-width:2px
style MP stroke:#8b5cf6,stroke-width:2px
style NS stroke:#ec4899,stroke-width:2px
style CT stroke:#f59e0b,stroke-width:2px
style LT stroke:#14b8a6,stroke-width:2px
真实的-#World choice_:}本博客
这个博客使用 审结表 供评论用的“.”方法
- 阅读意见的次数远多于书面
- 我们需要高效显示嵌套的批注线索
- 深度@- @% 限制查询对性能很重要 @ (_ @ we 上限为 @ I5# 深层 @ MPK3}
- 移动备注是罕见的 @( } moder 偶尔需要做此@ )_
见见 缩略语 完整执行细节@. @%
系列导航
- 部分=1:}概览 @(_这篇文章#)}
- 部分=1.1: 相邻列表 - 简单的父引用
- 缩略语 -预言关系
- 集成路径 -路径字符串
- {1.4:} 内嵌套件部件 @ -_ 左边 @ I/} 右邊界
- 部份 @1.5:_ltree - PostgreSQL 本地扩展
- “2:Raw SQL”和“Dapper ‘(’ ”部分即将到来