我已经开始阅读公用表表达式,并且无法想到我需要使用它们的用例。它们似乎是多余的,因为派生表也可以这样做。有什么我缺少或不理解?有人能给我一个简单的限制例子,通过常规的select,derived或temp表查询来说明CTE的情况吗?任何简单的例子都将受到高度赞赏。
答案 0 :(得分:175)
一个例子,如果您需要多次引用/加入相同的数据集,可以通过定义CTE来实现。因此,它可以是一种代码重用形式。
自引用的一个例子是递归:Recursive Queries Using CTE
激动人心的Microsoft定义 Taken from Books Online:
CTE可用于:
创建递归查询。有关详细信息,请参阅Recursive Queries Using Common Table Expressions.
在不需要一般使用视图时替换视图;也就是说,您不必将定义存储在元数据中。
通过从标量子选择派生的列或不具有确定性或具有外部访问权限的函数启用分组。
在同一语句中多次引用结果表。
答案 1 :(得分:43)
我使用它们来分解复杂的查询,尤其是复杂的连接和子查询。我发现我越来越多地使用它们作为“伪视图”来帮助我了解查询的意图。
我对他们的唯一抱怨是他们无法重复使用。例如,我可能有一个存储过程,其中包含两个可以使用相同CTE的更新语句。但CTE的“范围”仅是第一个查询。
麻烦的是,“简单的例子”可能并不真正需要CTE!
仍然非常方便。
答案 2 :(得分:36)
我认为使用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
答案 3 :(得分:16)
我发现使用CTE很有用的一个场景是当你想要根据一个或多个列获取DISTINCT数据行但返回表中的所有列时。使用标准查询,您可能首先必须将不同的值转储到临时表中,然后尝试将它们连接回原始表以检索其余列,或者您可以编写一个极其复杂的分区查询,该查询可以返回结果一次运行,但最有可能,它将是不可读的并导致性能问题。
但是使用CTE(由Tim Schmelter在Select the first instance of a record上回答)
WITH CTE AS(
SELECT myTable.*
, RN = ROW_NUMBER()OVER(PARTITION BY patientID ORDER BY ID)
FROM myTable
)
SELECT * FROM CTE
WHERE RN = 1
如您所见,这更容易阅读和维护。与其他查询相比,性能要好得多。
答案 4 :(得分:12)
将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;
享受
答案 5 :(得分:6)
今天我们将了解Common table表达式,它是SQL Server 2005中引入的新功能,也可以在以后的版本中使用。
公用表表达式: - 公用表表达式可以定义为临时结果集,换句话说,它可以替代SQL Server中的视图。公用表表达式仅在定义它的批处理语句中有效,不能在其他会话中使用。
声明CTE的语法(公用表表达式): -
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')
我创建了两个表employee和Dept,并在每个表中插入了5行。现在我想加入这些表并创建一个临时结果集以进一步使用它。
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”子句,然后我们给表表达式命名,这里我给出了名称“CTE_Example”
然后我们写“As”并将我们的代码括在两个括号(---)中,我们可以在封闭的括号中连接多个表。
在最后一行中,我使用了“Select * from CTE_Example”,我们在最后一行代码中引用了Common表达式,所以我们可以说它就像一个视图,我们在那里定义和使用视图在单个批处理中,CTE不作为永久对象存储在数据库中。但它的行为就像一个观点。我们可以在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”中删除数据。
答案 6 :(得分:3)
当您要执行“有序更新”时,它非常有用。
MS SQL不允许您将UPDATE与ORDER BY一起使用,但是在CTE的帮助下,您可以这样做:
WITH cte AS
(
SELECT TOP(5000) message_compressed, message, exception_compressed, exception
FROM logs
WHERE Id >= 5519694
ORDER BY Id
)
UPDATE cte
SET message_compressed = COMPRESS(message), exception_compressed = COMPRESS(exception)
在此处查看更多信息:How to update and order by using ms sql
答案 7 :(得分:0)
尚未指出的一点是速度。我知道这是一个古老的回答问题,但是我认为这应该直接发表评论/回答:
它们似乎是多余的,就像可以使用派生表一样
当我第一次使用CTE时,它的速度绝对让我震惊。就像教科书中的情况一样,非常适合CTE,但是在我曾经使用过CTE的 all 事件中,速度有了显着提高。我的第一个查询是复杂的派生表,需要很长时间才能执行。使用CTE花费了几秒钟的时间,这让我震惊,甚至有可能。
答案 8 :(得分:-3)
;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;
试试这个