FlyGoldfish

     说起递归查询,就不得不说CTE(Common Table Expression)公用表达式,CTE类似于派生表,其优点是与派生表不同,CTE能够引用自身。如果在查询时不想存储视图,可以用以一个CTE来代替它,在一个语句中还可以多次引用CTE。其实CTE递归查询和原子与分子查询有很多类似之处。原子查询建立一个表,而分子查询建立在原子查询之上,提供清晰与重复利用,应用也可以达到同样的目的,使用时可以将查询区域分割成可读的块,然后用这些块建立一个复杂的查询。

      递归的CTE是根据至少两个查询(或者用递归查询的说法成为成员)构建的一个是非递归查询,也称锚定成员(AM),也叫定点成员;另一个是递归查询,也称为递归成员(RM)。两个查询由Union All运算符链接为一个单独的CTE。

      递归查询的限制:

      1) 至少有一个定位点成员和一个递归成员,当然,你可以定义多个定位点成员和递归成员,但所有定位点成员必须在递归成员的前面。 
      2) 定位点成员之间必须使用UNION ALL、UNION、INTERSECT、EXCEPT集合运算符,最后一个定位点成员与递归成员之间必须使用UNION ALL,递归成员之间也必须使用UNION ALL连接。
      3) 定位点成员和递归成员中的字段数量和类型必须完全一致。
      4) 递归成员的FROM子句只能引用一次CTE对象。 
      5) 递归成员中不允许出现下列项 
           SELECT DISTINCT 
           GROUP BY 
           HAVING 
           标量聚合 
           TOP 
           LEFT、RIGHT、OUTER JOIN(允许出现 INNER JOIN) 
           子查询

     使用CTE时应注意以下几点:

     1.CTE后面必须直接跟使用CTE的SQL语句,否则,CTE将失效。

     2.CTE后面也可以跟其他CTE,但只能使用一个With,多个CTE中间用(,)分隔。

     3.如果CTE的表达式名称与某个数据表或视图重名,则紧跟在该CTE后面的SQL语句中使用的仍然是CTE,当然,后面的SQL语句中使用的表名就是指数据表或视图。

     4.CTE可以引用自身,也可以引用在同一With子句中预先定义的CTE。不允许向前引用。

     5.不能在CTE定义中使用以下语句:

          (1) Compte 或 Compute by

          (2) Order by (除非指定了Top子句)

          (3) into

          (4) 带有查询提示的Option子句

          (5) for xml

          (6) for browse

    6.如果将CTE用在属于批处理的一部分的语句,那么在它之前的语句必须以分号结尾。

    7.CTE除了可以简化嵌套SQL语句外,还可以进行递归调用。

       举例说明CTE的使用     

create table Area
(
  AreaCode int,
  AreaName nvarchar(20),
  ParentAreaCode int
)

insert Area values(1,\'亚洲\',0)
insert Area values(2,\'欧洲\',0)
insert Area values(3,\'美洲\',0)
insert Area values(101,\'中国\',1)
insert Area values(102,\'日本\',1)
insert Area values(103,\'韩国\',1)
insert Area values(101001,\'上海\',101)
insert Area values(101002,\'北京\',101)
insert Area values(101003,\'广州\',101)

   查询亚洲及其所属的地域     

with DF(AreaCode,AreaName,ParentAreaCode) as(
  select AreaCode,AreaName,ParentAreaCode from Area where AreaCode=1
  union all
  select a.AreaCode,a.AreaName,a.ParentAreaCode  from Area a,DF b where a.ParentAreaCode=b.AreaCode
)
select * from DF

  查询广州及其所在的地域

with DF(AreaCode,AreaName,ParentAreaCode) as(
  select AreaCode,AreaName,ParentAreaCode from Area where AreaCode=101003
  union all
  select a.AreaCode,a.AreaName,a.ParentAreaCode  from Area a,DF b where a.AreaCode=b.ParentAreaCode
)
select * from DF

  查询所有洲下所属的地域

with DF(AreaCode,AreaName,ParentAreaCode) as(
  select AreaCode,AreaName,ParentAreaCode from Area where ParentAreaCode=0
  union all
  select a.AreaCode,a.AreaName,a.ParentAreaCode  from Area a,DF b where a.ParentAreaCode=b.AreaCode
)
select * from DF

  

分类:

技术点:

相关文章: