简要描述:
当我们遇到一个需要通过递归查询才能实现的功能点时,首先想到的是代码递归去查询,但有时候我们会发现当树节点的层次比较多时,递归一次次的与数据库交互会一定程度的降低我们的程序性能(这还不包括一些节点子很功能的处理时间),基于此,我们可以考虑SQL递归,一次全查出来,或许能更好的的解决我们的问题。
问题情境:
设计了一张表,包含一个主键id,一个父ID,还有一些其他必要字段(简单说就是一个有树状结构的表),需要从这个表中查出某一个节点下所有直接子节点的id,然后用这个id获取一个表对象,经过一系列的计算与逻辑处理在将这些子节点的直接子节点查出来做同样的操作,直到查不到子节点为止。
问题解决:
根据问题情境很容易联想到代码的递归,先做查询在处理,一直递归下去,递归终止的条件就是查不到数据为止。实际开发过程中发现,这个递归很复杂,很耗时。因为不止一次的查询,查询出来的数据进行的逻辑处理不止有一些数据库交互还有很多耗时的接口调用,当数据量小时,速度还可以接受,但当树结构的层次变得很多时就会很慢。那么该如何解决这种问题呢?这里提供一个sql递归,一次性查出来一条分支的所有数据,然后用代码控制将最底层的数据计算结果一层层往上返回,最终得到根节点的计算结果。
废话不多说,下面是具体步骤:
首先上表结构
|
表中有一个ID字段,一个parentID字段,三个参数字段,一个节点等级字段(根节点为0,二级节点为1,以此类推)。
以上表中数据为例,树状图如下:
我们的目的是计算A001的值,而A001需要A002和A003的结果来参与计算,A002又需要A004和A005的计算结果,A003又需要A006和A007的计算结果,所以我们的思路是将A001下的所有子节点查出来,然后根据节点等级倒序排序。代码处理是将最底层的节点的计算结果放到方法入口的一个map中,这个map是从A001开始传进来的,所以这个map经过计算之后会将A004和A005的结果带回给A002并重新塞一个A002 的值进去,然后再带回给A001,A003的结果也是这样带回去的,所以A001得到的map中就包含它的所有子节点的值,可以直接拿来参与运算。
这就是一套计算流程,下面介绍一下递归SQL的写法。
假设我们上面的那个表叫testTable,那么我们的SQL如下:
with temp(ID,parentID,param1,param2,param3,level) as
(select ID,parentID,param1,param2,param3,level from testTable
where id ='A001'
union all
select A.ID,A.parentID,A.param1,A.param2,A.param3,A.level from testTable A ,temp B
where A.parentID=B.ID
)
select * from temp A order by A.level desc;
ok,我们的处理最终经过测试,效率并不差,而且代码实现比较容易(相比于代码的递归),当我们遇到有递归需求的表时,并不一定使用代码来递归,其实适当的选择sql递归也是不错的选择哦。