公用表表达式(CTE)的好处?


21

来自msdn

与派生表不同,CTE可以是自引用的,并且可以在同一查询中多次引用。

我已经大量使用了CTE,但是我从来没有考虑使用它们的好处。

如果我在同一查询中多次引用CTE:

  • 有什么性能上的好处?
  • 如果我正在执行自我联接,SQL Server将扫描目标表两次吗?

2
Profiler应该告诉您是否扫描两次。恕我直言,CTE非常适合递归。
丹·安德鲁斯

3
使用查询优化器时,没有硬性答案。有些查询将带来性能优势,而有些则不会。有时使用临时表代替CTE会更快,但有时不会。

Answers:


25

通常,CTE永远不会提高性能

CTE本质上是一次性的视图。没有存储其他统计信息,没有索引等。它用作子查询的简写。

在我看来,它们可能会被过度使用(我在工作中的代码中看到很多过度使用)。 这里有一些很好的答案,但是如果您需要多次引用某项内容,或者需要引用数十万行以上的内容,请将其放入#temp表中并为其建立索引。


3
同意。除递归CTE外,它们仅辅助可读性
gbn 2012年

如果CTE只返回几行(以便将它们保存在内存中),这些行的计算成本很高(在大表上进行聚合),并且该结果被多次使用,该怎么办?那应该提高性能,不是吗?(至少这是我在PostgreSQL和Oracle中很少使用临时表的经验)
a_horse_with_no_name 2012年

2
@a_horse_with_no_name-等同于使其成为子查询。如果结果在单个查询中多次使用,它将被重用且不会重新计算。如果在多个查询中使用了它,那么a CTE是一个不好的选择,因为结果将在第一个查询之后被丢弃。
JNK 2012年

@JNK:谢谢。似乎SQL Server在这里的行为有所不同。
a_horse_with_no_name 2012年

某些人在某些情况下发现CTE更具可读性FWIW stackoverflow.com/a/11170918/32453
rogerdpack

14

创建复杂的报表查询时,除了递归之外,我发现CTE极其有用的一个地方。我使用一系列CTE获取所需的数据块,然后将其合并到最终选择中。我发现它们比使用大量派生表或20个联接执行相同的操作更容易维护,而且我发现由于在数据库中的一对多关系,我可以放心,它返回正确的数据而不会产生多条记录。所有不同的联接。让我举一个简单的例子:

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

因此,通过分离出所需的不同信息块,您可以单独检查每个部分(使用注释掉的选择,通过单独取消对每个部分的注释,并且仅在选择的范围内运行),以及是否需要更改费用计算(在此示例中),要比将它们全部混合到一个大型查询中更容易找到。当然,我为此使用的实际报告查询通常比示例复杂得多。


1
仅用于报告查询?我每天工作的系统都有非常复杂的事务查询。奇怪的是,我们的报告查询通常是一些简单的查询。(当然,不包括琐碎的无连接CRUD查询)。
凯文·卡斯卡特

我用的例子,因为这些通常是最复杂,在这里
HLGEM

+1有时,逻辑性更好(人类可读)的查询比性能更好的查询更可取。
某天,2012年

是。鉴于CTE通常会产生相同的结果计划,所以我认为没有必要创建可怕的嵌套,多子查询怪异的东西-我们可以按照需要的顺序在视觉上布置每个组件。我导入XML文件并进行各种杂技操作,以将数据转换为正确的格式,如果没有CTE,则无法进行写入/读取。(我的某些旧代码可能全面都有可怕的子查询!)
underscore_d

0

与往常一样,这取决于性能,但是在某些情况下,性能会大大提高。我在INSERT INTO SELECT语句中看到了它,您在其中使用CTE进行选择,然后在INSERT INTO中使用它。这可能与为数据库设置RCSI有关,但是对于那些很少选择的时间,它可以提供很多帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.