何时使用公用表表达式(CTE)

ima*_*mak 219 sql t-sql sql-server common-table-expression

我已经开始阅读有关Common Table Expression的内容,并且无法想到我需要使用它们的用例.它们似乎是多余的,因为派生表可以做到这一点.有什么我缺少或不理解?有人能给我一个简单的限制例子,通过常规的select,derived或temp表查询来说明CTE的情况吗?任何简单的例子都将受到高度赞赏.

Joh*_*som 190

例如,如果需要多次引用/连接相同的数据集,则可以通过定义CTE来实现.因此,它可以是一种代码重用的形式.

自引用的一个示例是递归:使用CTE的递归查询

对于从联机丛书中获取的令人兴奋的Microsoft定义 :

CTE可用于:

  • 创建递归查询.有关更多信息,请参阅使用公用表表达式的递归查询.

  • 在不需要一般使用视图时替换视图; 也就是说,您不必将定义存储在元数据中.

  • 通过从标量子选择派生的列或不具有确定性或具有外部访问权限的函数启用分组.

  • 在同一语句中多次引用结果表.

  • 是的.您无法自行加入派生表.值得注意的是,自我加入CTE仍然会让你对它进行2次单独的调用. (6认同)
  • @cyberkiwi - 哪位?自我加入会导致两种不同的调用吗?请参阅本答案中的示例http://stackoverflow.com/questions/3362043/sql-server-row-present-in-one-query-missing-in-another/3362307#3362307 (4认同)
  • 有关CTE的有趣事实.我总是想知道为什么当CTE被多次引用时,CTE中的NEWID()会发生变化.`从master..spt_values顺序中选择前100*进入#tmp 1,2,3,4选择A.number,来自#tmp的COUNT(*)内连接#tmp B ON A.number = B.number + 1由A.number` vs`与CTE AS分组(从master..spt_values顺序中选择前100名*1,2,3,4)从CTE选择A.number,COUNT(*)A内连接CTE B ON A.由A.number`编号= B.number + 1组 (4认同)

n8w*_*wrl 47

我使用它们来分解复杂的查询,尤其是复杂的连接和子查询.我发现我越来越多地使用它们作为"伪视图"来帮助我了解查询的意图.

我对他们的唯一抱怨是他们无法重复使用.例如,我可能有一个存储过程,其中包含两个可以使用相同CTE的更新语句.但CTE的"范围"仅是第一个查询.

麻烦的是,"简单的例子"可能并不真正需要CTE!

还是,非常方便.

  • "我对他们的唯一抱怨是他们不能被重复使用" - 你想重复使用的CTE应该被认为是'VIEW`的候选者:) (28认同)
  • @onedaywhen:明白了,但这意味着全球范围我并不总是感到满意.有时在proc的范围内我想定义一个CTE并将其用于选择和更新,或者从不同的表中选择类似的数据. (6认同)
  • 当我不止一次需要相同的CTE时,我将它提供给一个临时表,然后尽可能多地使用临时表. (5认同)

Bri*_*anK 42

我看到使用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
Run Code Online (Sandbox Code Playgroud)

这是另一个我希望获得问题和答案列表的例子.我希望将答案与结果中的问题分组.

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
Run Code Online (Sandbox Code Playgroud)

  • 您的第一个示例是否可以简化为仅使用嵌套查询而不是CTE? (10认同)
  • 您应该添加没有CTE的第一个,然后立即显而易见为什么后者有用。 (3认同)
  • 两个例子都可以. (2认同)

The*_*Man 20

我发现使用CTE有用的一个场景是当你想要根据一个或多个列获取DISTINCT数据行但返回表中的所有列时.使用标准查询,您可能首先必须将不同的值转储到临时表中,然后尝试将它们连接回原始表以检索其余列,或者您可以编写一个极其复杂的分区查询,该查询可以返回结果一次运行,但最有可能,它将是不可读的并导致性能问题.

但是通过使用CTE(由Tim Schmelter回答选择记录的第一个实例)

WITH CTE AS(
    SELECT myTable.*
    , RN = ROW_NUMBER()OVER(PARTITION BY patientID ORDER BY ID)
    FROM myTable 
)
SELECT * FROM CTE
WHERE RN = 1
Run Code Online (Sandbox Code Playgroud)

如您所见,这更容易阅读和维护.与其他查询相比,性能要好得多.


Vic*_*Vic 15

将CTE视为用于单个查询的视图的替代可能更有意义.但是不需要正式视图的开销,元数据或持久性.当您需要时非常有用:

  • 创建递归查询.
  • 在查询中多次使用CTE的结果集.
  • 通过减少大块相同的子查询来提高查询的清晰度.
  • 通过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;
Run Code Online (Sandbox Code Playgroud)

请享用


bsi*_*ide 8

当您想要执行“有序更新”时,它非常有用。

MS SQL 不允许您将 ORDER BY 与 UPDATE 一起使用,但在 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)
Run Code Online (Sandbox Code Playgroud)

在这里查看更多信息:How to update and order by using ms sql


Nee*_*dav 6

今天我们将了解Common表表达式,它是SQL Server 2005中引入的新功能,也可以在更高版本中使用.

公用表表达式: - 公用表表达式可以定义为临时结果集,换句话说,它可以替代SQL Server中的视图.公用表表达式仅在定义它的批处理语句中有效,不能在其他会话中使用.

声明CTE的语法(公用表表达式): -

with [Name of CTE]
as
(
Body of common table expression
)
Run Code Online (Sandbox Code Playgroud)

让我们举一个例子: -

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')
Run Code Online (Sandbox Code Playgroud)

我创建了两个表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
Run Code Online (Sandbox Code Playgroud)

让我们逐一采用声明的每一行并理解.

要定义CTE,我们编写"with"子句,然后我们给表表达式命名,这里我给出了名称为"CTE_Example"的名称

然后我们写"As"并将我们的代码括在两个括号(---)中,我们可以在封闭的括号中连接多个表.

在最后一行中,我使用了"Select*from CTE_Example",我们在最后一行代码中引用Common表达式,所以我们可以说它就像一个视图,我们在一个视图中定义和使用视图batch和CTE不作为永久对象存储在数据库中.但它的行为就像一个观点.我们可以在CTE上执行删除和更新语句,这将对CTE中使用的引用表产生直接影响.让我们举个例子来理解这个事实.

With CTE_Example(EID,DeptName)
as
(
select EID,DeptName from DEPT 
)
delete from CTE_Example where EID=10 and DeptName ='Payroll'
Run Code Online (Sandbox Code Playgroud)

在上面的语句中,我们从CTE_Example中删除一行,它将从CTE中使用的引用表"DEPT"中删除数据.