何时使用通用expression式(CTE)
我已经开始阅读关于公共表expression式的内容 ,并且不能想到需要使用它的用例。 他们似乎是多余的,因为派生表可以做同样的事情。 有什么我失踪或理解不好? 有人可以给我一个简单的例子限制常规select,派生或临时表查询,使CTE的情况下? 任何简单的例子将不胜感激。
例如,如果您需要多次引用/连接相同的数据集,则可以通过定义CTE来实现。 因此,它可以是代码重用的一种forms。
自引用的一个例子是recursion: 使用CTE的recursion查询
对于令人兴奋的微软定义采取从联机丛书:
CTE可以用来:
-
创build一个recursion查询。 有关更多信息,请参阅使用公用表expression式的recursion查询。
-
当不需要视图的一般使用时,replace视图; 也就是说,您不必将定义存储在元数据中。
-
通过从标量子查询派生的列或者非确定性或外部访问的函数启用分组。
-
在同一个语句中多次引用结果表。
我用它们来分解复杂的查询,尤其是复杂的连接和子查询。 我发现我越来越多地使用它们作为“伪视图”来帮助我解决查询的意图。
我唯一的抱怨就是不能重复使用。 例如,我可能有一个存储过程与两个更新语句,可以使用相同的CTE。 但是,CTE的“范围”只是第一个查询。
麻烦的是,“简单的例子”可能并不需要CTE!
仍然非常方便。
我看到使用cte的原因有两个。
在where子句中使用计算的值。 这似乎比派生表清洁一点。
假设有两个表 – 问题和答案一起通过Questions.ID = Answers.Question_Id(和测验id)
WITH CTE AS ( Select Question_Text, (SELECT Count(*) FROM Answers A WHERE A.Question_ID = Q.ID) AS Number_Of_Answers FROM Questions Q ) SELECT * FROM CTE WHERE Number_Of_Answers > 0
这里有另一个例子,我想得到一个问题和答案的列表。 我希望将答案与结果中的问题分组。
WITH cte AS ( SELECT [Quiz_ID] ,[ID] AS Question_Id ,null AS Answer_Id ,[Question_Text] ,null AS Answer ,1 AS Is_Question FROM [Questions] UNION ALL SELECT Q.[Quiz_ID] ,[Question_ID] ,A.[ID] AS Answer_Id ,Q.Question_Text ,[Answer] ,0 AS Is_Question FROM [Answers] A INNER JOIN [Questions] Q ON Q.Quiz_ID = A.Quiz_ID AND Q.Id = A.Question_Id ) SELECT Quiz_Id, Question_Id, Is_Question, (CASE WHEN Answer IS NULL THEN Question_Text ELSE Answer END) as Name FROM cte GROUP BY Quiz_Id, Question_Id, Answer_id, Question_Text, Answer, Is_Question order by Quiz_Id, Question_Id, Is_Question Desc, Name
我发现对于使用CTE很有用的场景之一是当您想要获取基于一列或多列的DISTINCT行数据,但返回表中的所有列时。 使用标准查询,您可能需要首先将不同的值转储到临时表中,然后尝试将它们加回到原始表中以检索其余列,或者可以编写一个非常复杂的分区查询,以便返回结果一个运行,但最有可能的是,它将是不可读的,并导致性能问题。
但是通过使用CTE(如Tim Schmelter在selectlogging的第一个实例时所回答的)
WITH CTE AS( SELECT myTable.* , RN = ROW_NUMBER()OVER(PARTITION BY patientID ORDER BY ID) FROM myTable ) SELECT * FROM CTE WHERE RN = 1
正如你所看到的,这是更容易阅读和维护。 与其他查询相比,性能要好得多。
也许更有意义的是将CTE视为用于单个查询的视图的替代。 但是不需要正式视图的开销,元数据或持久性。 当你需要时非常有用:
- 创build一个recursion查询。
- 在查询中多次使用CTE的结果集。
- 通过减less大块相同的子查询来提高查询的清晰度。
- 通过CTE结果集中派生的列启用分组
下面是一个剪切和粘贴的例子:
WITH [cte_example] AS ( SELECT 1 AS [myNum], 'a num' as [label] UNION ALL SELECT [myNum]+1,[label] FROM [cte_example] WHERE [myNum] <= 10 ) SELECT * FROM [cte_example] UNION SELECT SUM([myNum]), 'sum_all' FROM [cte_example] UNION SELECT SUM([myNum]), 'sum_odd' FROM [cte_example] WHERE [myNum] % 2 = 1 UNION SELECT SUM([myNum]), 'sum_even' FROM [cte_example] WHERE [myNum] % 2 = 0;
请享用
今天我们要学习一下公共表expression式,这是SQL Server 2005中引入的一个新function,也可以在以后的版本中使用。
公用表expression式: – 公用表expression式可以被定义为一个临时结果集,换句话说,它可以被SQL Server中的视图替代。 公用表expression式仅在定义语句的批处理中有效,并且不能在其他会话中使用。
声明CTE的语法(公用表expression式): –
with [Name of CTE] as ( Body of common table expression )
让我们举个例子:
CREATE TABLE Employee([EID] [int] IDENTITY(10,5) NOT NULL,[Name] [varchar](50) NULL) insert into Employee(Name) values('Neeraj') insert into Employee(Name) values('dheeraj') insert into Employee(Name) values('shayam') insert into Employee(Name) values('vikas') insert into Employee(Name) values('raj') CREATE TABLE DEPT(EID INT,DEPTNAME VARCHAR(100)) insert into dept values(10,'IT') insert into dept values(15,'Finance') insert into dept values(20,'Admin') insert into dept values(25,'HR') insert into dept values(10,'Payroll')
我创build了两个表employee和Dept,并在每个表中插入了5行。 现在我想join这些表格并创build一个临时结果集以进一步使用它。
With CTE_Example(EID,Name,DeptName) as ( select Employee.EID,Name,DeptName from Employee inner join DEPT on Employee.EID =DEPT.EID ) select * from CTE_Example
让我们一个一个的理解每一句话。
为了定义CTE,我们编写了“with”子句,然后给表格expression一个名字,在这里我给出了名字“CTE_Example”
然后我们写“As”并把我们的代码放在两个括号(—)中,我们可以在括号中join多个表格。
在最后一行中,我使用了“Select * from CTE_Example”,我们在最后一行代码中引用了Commonexpression式,所以我们可以说它就像一个视图,我们在这里定义和使用视图批处理和CTE不作为永久对象存储在数据库中。 但它performance得像一个观点。 我们可以在CTE上执行删除和更新语句,这将直接影响正在CTE中使用的引用表。 让我们举一个例子来理解这个事实。
With CTE_Example(EID,DeptName) as ( select EID,DeptName from DEPT ) delete from CTE_Example where EID=10 and DeptName ='Payroll'
在上面的语句中,我们从CTE_Example中删除一行,它将从CTE中正在使用的引用表“DEPT”中删除数据。
我通常使用CTE创build视图,我不能使用本地或全局临时表。
;with cte as ( Select Department, Max(salary) as MaxSalary from test group by department ) select t.* from test t join cte c on c.department=t.department where t.salary=c.MaxSalary;
尝试这个