关系型数据库树形隶属关系表设计方案

2017-3-22 更新,在 Rails 中还有更便捷的做法:
在 org 表中添加一个 parent_id 字段,用以自引用。
org model 中这样写关联关系
belongs_to :parant_org, class_name: "Org", foreign_key: "parent_id"
has_many :sub_orgs, class_name: "Org", foreign_key: "parent_id"

层级隶属关系的业务场景在开发过程中很常见,譬如大型集团有总部、城市分公司、地区代理商及细化到门店这种不同层级形成的树状结构。最近系统接入了某个连锁美容品牌,其层级结构为:总部、地区分公司和门店。

设计理论

每个层级的个体在数据库表中都对应 orgs 记录,在层级关系中只有两种关系,一个 org 对于上层是;对于上层为hierarchies 表记录其在层级隶属关系中的角色并与之对应的上下级的记录。hierarchies 表有一个 type 字段,值为 RootParentSub,用以标记记录对于上下级关系的角色类型;hierarchy_id 字段与 orgs 表关联,标记当前的 org 记录;org_id 字段同样与 orgs 表的记录关联,用以找出当前 org 记录与type 字段对应的上级或下级的一个或多个 org 记录。 举个例子,新建一个位于根结点的总部 org 记录 A(id:1),隶属根系处于根结点不需在 hierarchies 表中添加记录。继续添加属于总部 A 的子节点 orgs 表记录 A_1(id:2) ,同时在 hierarchies 表添加三个记录,分别为对于 A_1 记录的父节点H_1(id: 1)和根节点H_2(id:2),另外一个记录是对于 A 记录的子节点H3(id: 3)

hierarchies 记录 org_id type hierarchy_id
hierarchy_a 1 Root 2
hierarchy_b 1 Parent 2
hierarchy_c 2 Sub 1

当前 org 记录通过外键 hierarchy_idhierarchies 表关联查找到与之对于的有层级关系的记录;然后通过 type 字段缩小范围,找到指定关系的记录,最后通过外键 org_id 找到对应隶属关系的记录在 orgs 表的记录。

结合 Rails 当表继承的实现

单表继承 基本概念在这里就不赘述了。

class Org < ApplicationRecord
has_one :root_hierarchy, class_name: "RootHierarchy", foreign_key: "hierarchy_id"
has_one :parent_hierarchy, class_name: "ParentHierarchy", foreign_key: "hierarchy_id"
has_many  :sub_hierarchies, class_name: "SubHierarchy", foreign_key: "hierarchy_id"
......
end

RootHierarchy, ParentHierarchy, SubHierarchy 继承了 Hierarchy model

class Hierarchy < ApplicationRecord
belongs_to  :org, class_name: "Org", foreign_key: "org_id"
belongs_to  :hierarchy, class_name: "Org", foreign_key: "hierarchy_id"
......
end

新建根结点的 org 不需在 hierarchies 表新建对于的记录,新建 org 的子节点需要在 hierarchies 表同时新建三个记录。

parent_org = Org.find(hierarchy.to_i)
@object = Org.new(params[:org])
if @object.save
  if parent_org
    ActiveRecord::Base.transaction do
      RootHierarchy.create!(org: parent_org.root_hierarchy.present? ? parent_org.root_hierarchy.org : parent_org, hierarchy: @object)
      ParentHierarchy.create!(org: parent_org, hierarchy: @object)
      SubHierarchy.create!(org: @object, hierarchy: parent_org)
    end
  end

列出某个某个的所有根、父、子节点

table.table.table-bordered.table-striped
- if model.root_hierarchy.present?
  tr
    td.prompt 根商户
    td.inp= model.root_hierarchy.org.name
- if model.parent_hierarchy.present?
  tr
    td.prompt 父商户
    td.inp= model.parent_hierarchy.org.name
- if model.sub_hierarchies.present?
  tr
    td.prompt 子商户
    td.inp= model.sub_hierarchies.includes(:org).map { |e| "#{e.org.name}, "  }
0 条评论
您想说点什么吗?