有关表表达式的相关知识点
有关表表达式的相关知识点
概念:表表达式是一种命名的查询表达式,代表一个有效的关系表。可以像其他表一样,在数据处理语句中使用表表达式。MSSQLServer支持4种表表达式:派生表(derived table)、公用表表达式(CTE,common table expression)、视图(View)、内联表值函数(inline TVF,inline table-valued function);
派生表:派生表示在外部查询的from子句中定义的,派生变的存在范围是定义它的外部查询,只要外部查询一结束,派生表就不存在了; 例如下面的代码就演示了派生表的基本语法:
1 use MyDB 2 go 3 select * 4 from 5 ( 6 select stuId, stuname, stuAge 7 from student 8 where stuId between 1 and 5 9 ) as stuInfo
需要注意的是,要像正确并有效的定义表表达式,查询语句必须满足以下三个要求:
1、不保证有一定的顺序;这就要求在查询子句中不能出现order by子句,因为表表达式代表的是一个表,而关系表中的行是没有固定顺序的!(例外:当语句中还指定了top的时候,可以使用order by;不过此时的order by 的目的是为top选项定义要筛选的行)
2、所有的列必须有名称;也就是说,用于定义表表达式的查询语句中,必须为select列表中的所有列必须有列名!像下面的代码就是错误的:
1 use MyDB 2 go 3 select * 4 from 5 ( 6 select stuId, stuname, stuAge,1+1 7 from student 8 where stuId between 1 and 5 9 ) as stuInfo
因为列"1+1"是一个无名列,如果单独执行子句则完全没有问题,但是表表达式中就必须为每一个列起名。。(如果没有列名,还怎么别外层的查询语句查找到呢?)
3、所有的列名必须是唯一的;这个容易理解吧。。就不多解释了。。只提一点:当定义语句联接了多个表时,又确实包括两个同名列的时候,就要为它们起不同的列名!
表表达式的一个好处是:在外部查询的任何子句中都可以引用在内部查询的select子句中分配的列别名;这解决了有关逻辑顺序优先级高于select的其他查询子句无法访问到别名的问题!!上例子:
1 select stuid as sid,count(*) as 数量 2 from 3 student 4 group by stuid
此例会出错的原因在于,group by子句引用了select子句中定义的别名sid,而group
by子句在逻辑上市先于select子句处理的;
这样,利用表表达式就可以解决这个问题(定义一个派生表stu):
1 select sid,count(Num) as amount 2 from 3 ( 4 select stuid as sid,stuname as Num 5 from 6 student 7 ) as stu 8 group by sid
也许这里无法看出表表达式的好处,但是当查询语句很长的时候,效果就明显了。。
这样写之后,sql server在执行时会扩展表表达式的定义,以便访问底层数据,扩展后的代码:
1 select stuid as sid,count(stuname) as amount 2 from student 3 group by stuid
也就是说,表表达式的使用是处于逻辑的原因,与性能无关,一般来说,表表达式既不会对性能产生正面的影响,也不会产生负面影响;
嵌套使用:
如果需要用一个本身就引用了某个派生表的查询区定义另一个派生表,最终得到的就是嵌套派生表,之所以会嵌套,是因为在外部查询的from子句中定义了派生表,而不是单独定义的,上例子:
1 select name 2 from 3 ( 4 select id,name 5 from 6 ( 7 select stuid as id,stuname as name 8 from student 9 where stuId between 1 and 5 10 )as stu1 11 )as stu2 12 where id = 3
公用表表达式(CTE):
公用表表达式是和派生表很相似的另一种形式的表表达式,而且具有一些重要的优势,CTE是用with子句定义的,一般格式为:
1 with <CTE_Name>[(<target_column_list>)] 2 as 3 ( 4 <inner_query_defining_CTE> 5 ) 6 <outer_query_against_CTE>; 7 和派生表一样,一旦外部查询完成,CTE的生命周期也就结束了。。: 8 with stuInfo as 9 ( 10 select stuid,stuname 11 from student 12 where stuId between 1 and 5 13 ) 14 select * from stuInfo
上面的例子定义了一个名为stuInfo的CTE,内部查询返回学号、姓名,外部查询则返回CTE的所有行。。
从表面来看,派生表和CTE之间的区别可能只是语义方面,不过,当你第一次定义并使用CTE后,就会发现,CTE具有几个重要的优势:
优势一:如果须要在一个CTE钟引用另一个CTE,不需要像派生表那样进行嵌套,只要简单的在同一个with子句中定义多个CTE,并用逗号隔开;每个CTE都可以引用在它之前定义的所有CTE,而外部查询则可以引用所有的CTE,下面的例子就与前面的派生表嵌套实现了同样的功能:
1 with stu1 as 2 ( 3 select stuid as id,stuname as name 4 from student 5 where stuId between 1 and 5 6 ), 7 stu2 as 8 ( 9 select id,name 10 from stu1 11 ) 12 select name from stu2 13 where 14 id = 3
优势二:CTE是先定义,再查询,所以对于外部的from子句来说,CTE是已经存在的,因此,可以引用同一个CTE的多个实例。
优势三:CTE 支持递归查询,这属于表表达式的高级用法,本菜鸟就不班门弄斧了。。只是把格式列如下:
1 with <CTE_Name>[(<target_column_list>)] 2 as 3 ( 4 <anchor_member> 5 union all 6 <recursive_member> 7 ) 8 <outer_query_against_CTE>