sql查询树结构,递归查询子节点效率慢的改进方法

说明

表达能力有限。有说的不清的地方可留言。有错误的地方请指正。

遇到的问题

有一个需求是查询一个树结构表的一个节点的所有子节点。(sqlserver)

表名 sys_kjfl 字段 bmid(子节点) parentID(父节点)

常规的查询所有后代节点的方法,都是使用递归 如这个链接: link.
于是我也使用这个 ,写了如下图的sql

with temp as (select bmid,parentID from sys_kjfl where bmid='1hxk'
           union all
           select a1.bmid,a1.parentID  from sys_kjfl a1 
           join temp  on  a1.parentID = temp.bmid
         ) 
  select * from temp 

就是找出 ‘1hxk’这个节点的所有后代节点,当数据量小的时候查询速度没有感觉,但是现在这个节点下面有接近3000个后代节点,并且树高为5,查询效率很慢,需要3秒,这个不可接受。

解决办法

我在想效率慢的原因是什么,无非是递归次数以及每一次连接的两表数据量的原因。我想到单查一个节点的祖先节点要快一些可不可以从这个角度尝试一下,当然这都是一些毫无计算的猜测,但还是需要尝试一下,写了如下的sql

with temp as (select bmid as t,bmid,parentID from sys_kjfl 
           union all
           select temp.t,a1.bmid,a1.parentID from sys_kjfl a1 
           join temp  on  a1.bmid = temp.parentID
         ) select t from temp where bmid='1hxk' 

果然不到一秒就查出来了。其中 t 就是 '1hxk’节点的所有后代节点。与普通的递归不同 我多写了一个 t字段,而且条件写在外面。这样相当于对所有的节点都做了查找祖先节点,然后筛选,我觉得困惑的地方就在于 我感觉对所有节点做递归查找祖先节点和对一个节点查找后代节点差不多啊,具体的复杂度的计算 再做尝试。

解决办法的sql的解释

其实就是换一个方式查子孙节点而已

1》查询一个节点的祖先节点通常是这样子

with temp as (select bmid,parentID from sys_kjfl where bmid='n391' 
           union all
           select a1.bmid,a1.parentID from sys_kjfl a1 
           join temp  on  a1.bmid = temp.parentID
         ) select bmid from temp 

查询结果 中bmid 就是 祖先节点的集合。

2》在查询结果加上 “这是哪个节点的祖先”如下

with temp as (select bmid as t,bmid,parentID from sys_kjfl where bmid='n391'
           union all
           select temp.t,a1.bmid,a1.parentID from sys_kjfl a1 join temp  on a1.bmid = temp.parentID
         ) 
         select t,bmid from temp 

查询结果中 t字段 就是‘n391’,bmid就是祖先节点。就是说查询结果中 bmid字段是t字段的祖先节点。
3》 去掉限制字段 查询所有节点的祖先节点

with temp as (select bmid as t,bmid,parentID from sys_kjfl 
           union all
           select temp.t,a1.bmid,a1.parentID from sys_kjfl a1 join temp  on a1.bmid = temp.parentID
         ) 
         select t,bmid from temp 

查询结果中 bmid字段是t字段的祖先节点。换言之t字段就是是 bmid字段的子孙节点。
4》在结果中加上查询条件

with temp as (select bmid as t,bmid,parentID from sys_kjfl 
           union all
           select temp.t,a1.bmid,a1.parentID from sys_kjfl a1 
           join temp  on  a1.bmid = temp.parentID
         ) select t from temp where bmid='1hxk' 

查询结果为’1hxk’的所有子孙节点。这样就实现了查询一个节点的所有子孙节点的另一种方式。而且效率更高。

时间复杂度

在做数据库查询的时候,我觉得join 操作是最影响效率的,假设两个表table1 和 table2 ,table1记录数为n,table2记录数为m,join需要table1中的每一条记录去匹配table2中的每一条记录,那么join的时间复杂度就是O(mn)。
以下称时间较快的查询子孙节点的为sql1
时间较慢的传统的查询子孙节点为sql2
如果表的记录书为n,sql1 中复杂度是O(n
n),sql2也是O(n*n)这就奇怪了。我计算复杂度是将递归查询转化为普通的查询计算的,我也认为sqlserver是这样处理递归语句的。
为了搞清楚,我把sql2转化为如下的普通查询

with t1 as(select bmid,parentID from sys_kjfl where bmid='1hxk')
,t2 as (select a1.bmid,a1.parentID  from sys_kjfl a1 join t1 a2  on a1.parentID = a2.bmid)
,t3 as (select a1.bmid,a1.parentID  from sys_kjfl a1 join t2 a2  on a1.parentID = a2.bmid)
,t4 as (select a1.bmid,a1.parentID  from sys_kjfl a1 join t3 a2  on a1.parentID = a2.bmid)
,t5 as (select a1.bmid,a1.parentID  from sys_kjfl a1 join t4 a2  on a1.parentID = a2.bmid)
select * from t1
union all
select * from t2
union all
select * from t3
union all
select * from t4
union all
select * from t5

神奇的事情发生了,这个查询也不到一秒就查出来了。看来事实和我想的不一样,sqlserver到底对递归查询做了怎样的处理呢,我还没找到参考资料。从我的角度看,我认为sql1和sql2不应该有这么大的效率差别,但事实就是有很大差别。我认为这是sqlserver的一个bug。

你可能感兴趣的:(小学生)