WITH
<cte_name> [(<target_col_list>)]
AS
(
<定义CTE的内部查询>
)
<对CTE进行外部查询>;
CTE的语法,从WITH开始,首先在括号里定义CTE内部查询,然后在外部查询引用CTE的名称。
对CTE的内部查询表达式,有如下规则:
1.查询必须是一个有效的表;
2.所有的列必须要有名称;
3.所有的列名必须唯一;
use
Northwind;
go
with
EmployeeYearOrder
as
(
select
(e.FirstName
+
N
'
'
+
e.LastName)
as
employeename,
year
(o.orderdate)
as
theyear,
COUNT
(o.OrderID) ordernum
from
dbo.Orders o
inner
join
dbo.Employees e
on
o.EmployeeID
=
e.EmployeeID
group
by
e.FirstName
+
N
'
'
+
e.LastName,
year
(o.orderdate)
)
select
*
from
EmployeeYearOrder cur
left
join
EmployeeYearOrder pre
on
cur.theyear
=
pre.theyear
+
1
上边的例子是多引用的CTE,利用CTE定义了每年的雇员的订单的数目,在外部查询中,通过两次引用EmployeeYearOrder ,一个代表当前年份,一个代表上一年。
如果使用派生表的话,代码是如下组织的:
use
Northwind;
go
select
*
from
(
select
(e.FirstName
+
N
'
'
+
e.LastName)
as
employeename,
year
(o.orderdate)
as
theyear,
COUNT
(o.OrderID) ordernum
from
dbo.Orders o
inner
join
dbo.Employees e
on
o.EmployeeID
=
e.EmployeeID
group
by
e.FirstName
+
N
'
'
+
e.LastName,
year
(o.orderdate)
)
as
cur
left
join
(
select
(e.FirstName
+
N
'
'
+
e.LastName)
as
employeename,
year
(o.orderdate)
as
theyear,
COUNT
(o.OrderID) ordernum
from
dbo.Orders o
inner
join
dbo.Employees e
on
o.EmployeeID
=
e.EmployeeID
group
by
e.FirstName
+
N
'
'
+
e.LastName,
year
(o.orderdate)
)
as
pre
on
cur.theyear
=
pre.theyear
+
1
;
注意上边的查询,核心查询的部分重复了两次。查询越复杂,引用次数越多,基于CTE的解决方案越有优势。当然从性能上来讲,这两种写法,经过查询优化器分析后,最终都得到同样的执行计划。
还有多CTE的情况。CTE不允许直接嵌套,但是可以用同一个WITH定义多个CTE,从而得到和嵌套派生表相同的效果,但是却没有嵌套派生表那么复杂。
看下边的查询:
with
c1
as
(
select
YEAR
(orderdate)
as
theyear,(e.FirstName
+
N
'
'
+
e.LastName)
as
employeename,OrderID
from
dbo.Orders
as
o
inner
join
dbo.Employees e
on
o.EmployeeID
=
e.EmployeeID
),
c2
as
(
select
theyear,employeename,
COUNT
(OrderID) ordernum
from
c1
group
by
theyear,employeename
)
select
employeename,theyear,ordernum
from
c2;
就是一个多CTE的例子,看起来比多层嵌套的派生表要直观。
CTE最大的用处,我觉得还是递归查询。
还是给出一个例子。
WITH
Emps
AS
(
SELECT
empid, mgrid, firstname, lastname
FROM
HR.Employees
WHERE
empid
=
5
UNION
ALL
SELECT
Emp.empid, Emp.mgrid, Emp.firstname, Emp.lastname
FROM
Emps
AS
Mgr
JOIN
HR.Employees
AS
Emp
ON
Emp.mgrid
=
Mgr.empid
)
SELECT
*
FROM
Emps;
这段查询将返回每位经理的直接下属。
如上查询所示,递归的CTE,必须包含至少两个查询。第一个查询被成为定位点成员,它只是一个返回有效表的查询,作为递归的基础或定位点。而第二个查询则成为递归成员,是该查询成为递归成员的是对CTE名称的递归引用。如果担心循环的发生,则可以指定option(maxrecursion n)来限制递归成员的调用次数。关于CTE的更多应用和深入理解,有机会再深入去讲解。
对于T-SQL公用表表达式,就讲到这里,有什么问题,还请大家指出,一起探讨。
参考文献:《Microsoft SQL Server 2008 技术内幕:T-SQL查询》