开篇:项目中用到上下级从属关系的太多太多了,如:组织、分类、行政区域,这里不再一一介绍,遇到这种的如何去进行数据库表的设计及其应用的,个人对往期项目中所涉及到的进行了一些总结。

数据库表设计:表字段一般含有:ID,Code,Name,ParentCode,ParentName,CodePath,NamePath,Level,IsNotLast,这里解释一下CodePath,NamePath,主要是为了后续方便查询使用,Level是为了方便层级检索,IsNotLast是是否最后一级,这个作用在行政区域、组织层级中作用比较大,分类中作用不大。

既然有CodePath,NamePath那么问题来了,如何自动生成这两个值呢,可以建触发器也可以把这两列做成计算列,这里以触发器为例:

触发器前期条件:1.必须有根节点,这个根节点是逻辑根节点,在进行逻辑判断及查询时方便操作,可能在实际中没有作用;

2.插入数据时必须保证父节点存在,如果没有父节点就以逻辑根节点为父节点。

在满足以上量个前提下,可以继续下一步操作:

光说不练假把式,直接上代码:

建表语句:

------创建表Tree
if not exists(select * from sysobjects where name='Tree')
begin
create table Tree(ID int identity(1,1) primary key,Code varchar(32),Name nvarchar(128),
ParentCode varchar(32) ,ParentName nvarchar(32),CodePath nvarchar(1024),NamePath nvarchar(1024),Level int,IsNotLast int);
insert into Tree values ('','中国',null,null,null,null,0,0);--创建根节点 中国是根节点
end
else
begin
drop table Tree
create table Tree(ID int identity(1,1) primary key,Code varchar(32),Name nvarchar(128),
ParentCode varchar(32) ,ParentName nvarchar(32),CodePath nvarchar(1024),NamePath nvarchar(1024),Level int,IsNotLast int);
insert into Tree values ('','中国',null,null,null,null,0,0);--创建根节点 中国是根节点
end
go

创建触发器

--自动生成CodePath,NamePath --建立触发器,或者自动计算列均可,这里创建触发器,每次插入或者更新时根据子节点找父节点,层层向上找到并自动更新CodePath,NamePath两列
--此触发器有一个缺陷,比如你仅仅更新了父节点,但是该父节点下的子节点的NamePath,CodePath不会自动更新
--这里不再讨论全局更新问题,后续在进行介绍,可以根据根节点,层层向下找到子节点进行更新,或者定时自动更新
create trigger Trigger_Update_CodePathAndNamePathByCode_After_Insert_For_Tree
on Tree after insert,update
as set nocount on
DECLARE @ID int,
@Code varchar(32),
@ParentCode varchar(32),
@Name nvarchar(500),
@CodePath varchar(1024),
@NamePath nvarchar(1024);
--查询更新或插入的ID,CodePath(默认是Code),NamePath(默认是Name),ParentCode
DECLARE cur_Inserted CURSOR FOR SELECT ID FROM Inserted;--从Inserted表创建游标
OPEN cur_Inserted;
FETCH NEXT FROM cur_Inserted INTO @ID--,@Code,@ParentCode,@Name,@CodePath,@NamePath;
WHILE(@@Fetch_Status=0)
begin
select @ID = ID,@CodePath = Code,@NamePath = Name,@ParentCode = ParentCode from Tree where ID = @ID;
while (@ParentCode is not null)
begin
select @Code=Code,@Name=Name,@ParentCode=ParentCode from Tree where Code = @ParentCode;
set @CodePath = @Code + '\' + @CodePath;
set @NamePath = @Name + '\' + @NamePath;
if(@ParentCode = @Code)
set @ParentCode = null;
end
IF(@CodePath is null and @NamePath is null)
UPDATE [Tree] SET [CodePath]=NULL, [NamePath]=NULL WHERE [ID] = @ID;--单条记录
ELSE
UPDATE [Tree] SET [CodePath]=@CodePath, [NamePath]=@NamePath WHERE [ID] = @ID;--单条记录
FETCH NEXT FROM cur_Inserted INTO @ID --,@Code,@ParentCode,@Name,@CodePath,@NamePath;
end
close cur_Inserted
Deallocate cur_Inserted;
set nocount off;

插入测试数据

--创建触发器成功后插入其他非根节点数据
insert into Tree values ('','北京市','','中国',null,null,1,0);
insert into Tree values ('','市辖区','','北京市',null,null,2,0);
insert into Tree values ('','东城区','','北京市',null,null,3,0);
insert into Tree values ('','东华门街道','','东城区',null,null,4,1);
insert into Tree values ('','河北省','','中国',null,null,1,0);
insert into Tree values ('','石家庄市','','河北省',null,null,2,0);
insert into Tree values ('','市辖区','','石家庄市',null,null,3,0);
insert into Tree values ('','长安区','','石家庄市',null,null,3,0);
insert into Tree values ('','建北街道','','长安区',null,null,4,1);
insert into Tree values ('','行唐县','','石家庄市',null,null,3,0);
insert into Tree values ('','城寨乡','','行唐县',null,null,4,1);

递归查询某一个节点及其所有子节点,常规查询方法 就是递归查询

--递归查询   查询某一节点下所有子节点
with Trees(ID,Code,Name,ParentCode,CodePath,NamePath,Level,ZLevel)
as(select t.ID,t.Code,t.Name,t.ParentCode,t.CodePath,t.NamePath,t.Level,t.Level as ZLevel from Tree t
where t.ParentCode = '' --递归的首先找到根节点也就是根节点
union all
select t.ID,t.Code,t.Name,t.ParentCode,t.CodePath,t.NamePath,t.Level,ZLevel + 1 from Tree t--原表
join Trees t2 on t2.Code = t.ParentCode) --递归表与原表join,关系为通过递归表中的Code找到原表中的ParentCode成员调用
select * from Trees
order by Level,ZLevel
go

另一种算法就是按照Codepath来查询了,这里就知道CodePath,NamePath的重要性了,这个就比较简单了

select * from Tree where CodePath like '000000000%'—看来加了路径能方便检索

实际应用:实际应用中需要注意的几点

1.如果是行政区域,组织这些不轻易改动的,可以使用缓存或者根据数据库表生成json格式或其他常用格式的物理文件,能大大提高速度

2.对于改动比较大,且改动经常是层级改动:如原来的子级经常变化为平级或者父级,且改动量极大的,需要考虑触发器性能及修改降级或者升级后原来的子级如何变化,本触发器不涉及到原来的子级

最后总结:设计模式没有最好至少更合适,如果大家有其他思路,欢迎交流,可以直接回复评论或联系我,feifei12300@126.com,QQ:251608027

MSSQLServer中组织或分类表的设计及其递归查询的更多相关文章

  1. K8 系统中省市县数据表的设计可以反映出什么? 通过一个基础业务表的设计品味软件系统的整体架构

    1:没有严谨的Id思想,不变化的Id思想,看不见的Id的思想. 2:数据不严谨,没有上下级关系,没有树形结构,ParentId 的思想. 3:表之间的关系都是弱关联,基础数据一修改业务数据就容易乱套. ...

  2. MySQL技巧(二)——无限级分类表设计

    无限级分类表的设计(掌握'自身连接') 类似图书这种,会有很多种分类,而且在现实生活中这种分类会无限的往下分,所以不可能每有一个分类就创建一个分类表.应该使用下面这种语句 DROP TABLE IF ...

  3. 关于OA流程相关数据表的设计

    一.前言 近期有些同学问起流程的表设计,终于有时间能写下博客,并整理下之前所发布的文章. 之前的文章讲到的表设计,没有给全且还存在漏洞,在这里向各位同学表示歉意.这是我个人最新领悟的一些流程思维,欢迎 ...

  4. MySQL分类表设计--根据ID删除全部子类

    在做数据库分类表的时候,通常会有这样的设计:一个字段是ID,另一个字段PID,PID指向自己的上级分类: 这样的设计带来的问题是:我要删除一个类,我希望它的子类全部一起删除: 在不知道分类有多少层级的 ...

  5. Java连接MySQL数据库。编写一个应用程序,在主类Test_4类中,通过JDBC访问stu数据库,显示t_student表中的内容(表结构见表1),显示效果自己设计。

    题目2:编写一个应用程序,在主类Test_4类中,通过JDBC访问stu数据库,显示t_student表中的内容(表结构见表1),显示效果自己设计.之后,可根据显示的内容进行某条记录的删除(以id为条 ...

  6. Lua中table的实现-《Lua设计与实现》

    本文来自<Lua设计与实现>的阅读笔记,推荐Lua学习者可以购买一本,深入浅出讲解lua的设计和实现原理,很赞,哈哈   Lua中对于表的设计,是基于数组和散列表,和其他语言不同,对于数组 ...

  7. Atitit 支出分类表 会计科目(1)资产(2)负债(3)资本(4)收益(5)费用(成本) 资产分类表 attilax总结

    Atitit 支出分类表  会计科目(1)资产(2)负债(3)资本(4)收益(5)费用(成本)  资产分类表 attilax总结 会计科目对一般不懂会计的管理人员,常会有莫测高深的感觉,因此不仅不愿去 ...

  8. FlowPortal-BPM——创建新组织架构、表单、流程

    一.创建新组织架构 (1)管理流程→组织管理→组织架构添加需要的组织架构→新建新成员或角色 (2)设置成员信息 二.创建新数据源(如果在已有的数据库中操作,只需要添加需要的表) (1)添加新数据库并添 ...

  9. 树形结构的数据库表Schema设计-基于左右值编码

    树形结构的数据库表Schema设计 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门.栏目结构.商品分类等等,通常而言,这些树状结构需要借助于数据库完 成持久化.然而目前的 ...

随机推荐

  1. 日期关联取最近日期的SQL

    SQL怎么关联,如下图A表用日期加产品编号关联B表的时候,如果日期不存在,则取之前最近一个日期的值,比如A表2012-07-31 关联B表,B表没有对应日期的,就取2012-07-30的 A表     ...

  2. IOS之Objective-C学习 策略模式

    对于策略模式,我个人理解策略模式就是对各种规则的一种封装的方法,而不仅仅是对算法的封装与调用而已.与工厂模式中简单工厂有点类似,但是比简单工厂更有耦合度,因为策略模式以相同的方法调用所有的规则,减少了 ...

  3. JavaScript标准参考教材(alpha)--笔记

    一.导论 二.基本语法 1.严格来说var a=1与a=1效果不太一样,delete命令无法删除前者. JavaScirpt是一种动态类型语言,也就是说,变量的类型没有限制,可以赋予各种类型的值. J ...

  4. AngularJS 系列 01 - HelloWorld和数据绑定

    目录导读: AngularJS 系列 学习笔记 目录篇 前言: 好记性不如烂键盘,随笔就是随手笔记,希望以后有用. 本篇目录: 1. Hello World 2. AngularJS中的数据绑定 3. ...

  5. protobuf的编译安装

    github地址:https://github.com/google/protobuf支持多种语言,有多个语言的版本,本文采用的是在centos7下编译源码进行安装. github上有详细的安装说明: ...

  6. 华为手机浏览器不支持PUT提交方式的解决方案

    最近所在技术团队在开发webapp项目,前端angularjs+后端.Net MVC API,API登录接口定义为PUT提交方式,在做兼容测试时发现UC.safari.微信浏览器下都可以登录,但在华为 ...

  7. javascript函数setInterval和setTimeout的使用区别详解

    setTimeout和setInterval的使用 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.不过两者各有各的应用场景. 方 法 实际上,setTimeout和setIn ...

  8. C++点滴----关于类常成员函数

    关于C++中,类的常成员函数 声明样式为:   返回类型 <类标识符::>函数名称(参数表) const 一些说明: 1.const是函数声明的一部分,在函数的实现部分也需要加上const ...

  9. Leetcode 99: Recovery binary search tree 总算明白了算法, 把代码写清楚, 让错误无处可藏.

    想写点什么, 因为这道题花了我好几个小时, 在周日, 除了在球场上跑了二个小时, 就泡在这道题上面. read blogs: http://www.lifeincode.net/programming ...

  10. Struts2中的Ognl

    OGNL(Object-Graph Navigation Language)全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整 ...