在数据查询中,从2008开始SQL Server提供了一个新的数据类型hierarchyid,专门用来操作层次型数据结构。

hierarchyid  类型对层次结构树中有关单个节点的信息进行逻辑编码的方法是:对从树的根目录到该节点的路径进行编码。 

这种路径在逻辑上表示为一个在根之后被访问的所有子级的节点标签序列。 表示形式以一条斜杠开头,只访问根的路径由单条斜杠表示。 对于根以下的各级,各标签编码为由点分隔的整数序列。 子级之间的比较就是按字典顺序比较由点分隔的整数序列。 每个级别后面紧跟着一个斜杠。 因此斜杠将父级与其子级分隔开。 例如,以下是长度分别为 1 级、2 级、2 级、3 级和 3 级的有效 hierarchyid 路径:

• / 

• /1/ 

• /0.3.-7/ 

• /1/3/ 

• /0.1/0.2/

在没有hierarchyid的日子里,我们通过CTE的方式来查询父以及全部的下级,但是,数据量多的情况下,CTE的方式将会变的很慢,后来,我们通过构造PATH的方式来加快速度。那么,有了hierarchyid类型后,自然得使用hierarchyid了。

现在,通过一个实际的例子来看看hierarchyid的威力。

一:CTE方式

WITH CTEGetChild AS 

    SELECT * FROM EL_Organization.Organization WHERE ID='ecc43c7159924dca91e2916368f923f4' --and [State]=0 and AuditState=2
    UNION ALL 
     (
        SELECT A.* FROM EL_Organization.Organization AS A
        INNER JOIN CTEGetChild AS B ON a.PARENTID=B.ID  --and A.[State]=0 and A.AuditState=2
     ) 
)

查询出来4489行,需要25S。

看来CTE方式已经到了不能容忍的地步,那么,现在,我们就用它来进行优化。

二:hierarchyid

首先,我们得新建该字段,然后为其赋值,

create function f_cidname(@id varchar(50)) returns varchar(max) as
begin
declare @pids nvarchar(max);
declare @pNames nvarchar(max);
set @pids='';
set @pNames='';
with cte as
( select id,parentid,name from EL_Organization.Organization where id =@id--'00037fdf184e48d084b87c3499e3c0e5'
union all
select b.id,b.parentid,b.name from cte A ,EL_Organization.Organization B where a.parentid = b.id
)

select @pids=convert(varchar(32),Convert(int, Convert(varbinary(max), id)))  + '/'+ @pids from cte
return '/'+@pids

end
go

接着,我们需要Update全表:

UPDATE EL_Organization.Organization SET PIDS=dbo.f_cidname(id)

注意,id是guid的32位字符串,而hierarchyid字段不支持那么大的Path内路径,于是我们将GUID转为了整型:convert(varchar(32),Convert(int, Convert(varbinary(max), id)))

2.1 TIP

Exception message: DataReader.GetFieldType(4) returned null. Exception data: System.Collections.ListDictionaryInternal

注意,极有可能我们把字段更新上去后,我们的程序却出错了,如上。这个时候,我们需要把

C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Types.dll

这个DLL打包到我们的应用程序中去。原因不解释了。

看看效果吧,修改过后的代码为:

DECLARE @tmpIds hierarchyid
SELECT @tmpIds=Pids FROM EL_Organization.Organization WHERE ID='ecc43c7159924dca91e2916368f923f4';
WITH CTEGetChild AS  (
    SELECT * FROM EL_Organization.Organization WHERE ID='ecc43c7159924dca91e2916368f923f4'
    UNION ALL(
    SELECT * FROM EL_Organization.Organization WHERE Pids.IsDescendantOf(@tmpIds)=1
    )
)
SELECT * FROM CTEGetChild

现在,我们的时间到了1S内。

2.2 一切为了不动应用层代码

现在,既然,增加了一个字段,我们就要维护这个字段,如:本条记录在应用程序中被移动到了别的父级下,就需要更新这个字段。为了不动上层代码,唯一能做的就是创建触发器,即:原有的ParentId变动的时候,就需要更新这个PIds字段,于是,我们创建触发器如下:

create trigger UpdateOrgPIds
on EL_Organization.Organization
after update
as
if update ([ParentId])
begin
     declare @tmpId varchar(36)
     select @tmpId=id from inserted
     update EL_Organization.Organization set pids=dbo.f_cidname(@tmpId)
end
go
-- drop  trigger EL_Organization.UpdateOrgPIds

微信扫一扫,关注最课程,获取每日一练

SQL中树形分层数据的查询优化的更多相关文章

  1. sql 中实现打乱数据的排序

    sql 中实现打乱数据的排序    order by NEWID()就实现了数据的打乱 

  2. 在SQL中导入Excel数据时强制以文本类型导入

    Excel不是关系型数据库,在导入到sql中时对于数值型,sql有时int型会处理成float,有时数字文本混排的列,sql会认为是数值型,导入的结果有的数据变成了null,但是用sql导出excel ...

  3. SQL Server 中树形表数据的处理总结

    -- 使用函数的方法: --建立 演示环境 if object_id('tb_bookInfo') is not null drop table tb_bookInfo go ),type int) ...

  4. Sql中如何将数据表的两个字段的值如何互换?

    今天遇到一个数据表的两个列数据要互换,在网上找到并记录下. 直接用Sql就可以搞定,语法如下 --将数据表中两个列数据互换的语法-- update tabName set field1=field2, ...

  5. sql中--行处理数据的两种方式

    --创建待使用的表格CREATE TABLE Orders ( OrderID INT , CostValue DECIMAL(18, 2) );WITH cte_temp AS ( SELECT 1 ...

  6. ado.net 向sql中插入新数据的同时获取自增重的id值

    两种方法都可以实现: 要获取的自增长列为phonebookID 方法一: sql = "insert into phonebook (mobile,peoplename) output in ...

  7. 在SQL 中生成JSON数据

    这段时间接手一个数据操作记录的功能,刚拿到手上的时候打算用EF做,后来经过仔细考虑最后还是觉定放弃,最后思考再三决定: 1.以模块为单位分表.列固定(其实可以所有的操作记录都放到同一个表,但是考虑到数 ...

  8. 在SQL中使用CLR提供基本函数对二进制数据进行解析与构造

      二进制数据包的解析一般是借助C#等语言,在通讯程序中解析后形成字段,再统一单笔或者批量(表类型参数)提交至数据库,在通讯程序中,存在BINARY到struct再到table的转换. 现借助CLR提 ...

  9. sql中对查询出来的数据进行分页

    当sql中存储的数据量比较大时,在web中 数据显示时都会对数据进行分页,分页不会在客户端进行分页,而是在数据库查询过程中进行了分页. sql代码: DECLARE @pageindex INT; - ...

随机推荐

  1. BZOJ 1878 HH的项链 | 主席树

    题意 询问区间有多少不同的数. 题解 和Luogu 1903一样,这道题也是用pre数组来求区间不同数的个数,这里pre[i]表示a[i]上一次出现的位置 +1,询问相当于查询区间内有多少pre小于等 ...

  2. SSL单向认证和双向认证说明

    SSL单向认证和双向认证说明 一.SSL双向认证具体过程 浏览器发送一个连接请求给安全服务器. 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器. 客户浏览器检查服务器送过来的证书是否是由自己 ...

  3. Storm集群启动流程分析

    Storm集群启动流程分析 程序员 1.客户端运行storm nimbus时,会调用storm的python脚本,该脚本中为每个命令编写了一个方法,每个方法都可以生成一条相应的Java命令. 命令格式 ...

  4. HttpRequest中常见的四种Content-Type(转)

    add by zhj: Content-Type用于说明request body的编码格式的,对于没有request body的http method如GET,HEAD没有必要设置这个参数,当然,你设 ...

  5. InnoDB的锁机制浅析(一)—基本概念/兼容矩阵

    InnoDB锁的基本概念 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Record锁/Gap锁/Next-key ...

  6. Xamarin-Android_BaseAdapter 简单的复用

    Xamarin-Android_BaseAdapter 简单的复用 缘由: 本人是一枚 小菜 初学Xamarin-Android  正在学习ListView 控件 发现这个控件的自定义布局 用的那叫一 ...

  7. BZOJ.4819.[SDOI2017]新生舞会(01分数规划 费用流SPFA)

    BZOJ 洛谷 裸01分数规划.二分之后就是裸最大费用最大流了. 写的朴素SPFA费用流,洛谷跑的非常快啊,为什么有人还T成那样.. 当然用二分也很慢,用什么什么迭代会很快. [Update] 19. ...

  8. [HDU5714]拍照

    [HDU5714]拍照 题目大意: 河上有\(n(n\le10^4)\)个船只,小明希望把尽可能多的船只都完整地拍到一张照片中. 小明位于河的边上,并且可以在河边的任意位置进行拍照,照相机的视野恰好为 ...

  9. go标准库DOC与 raft

    http://studygolang.com/static/pkgdoc/index.html https://github.com/avelino/awesome-go#database

  10. Unity3D脚本(MonoBehaviour)生命周期

    场景中有2个物体:A,B 每一个物体上绑定2个脚本:A,B 初始化log: Object : A , Script : B , Message : Awake Object : A , Script ...