我如何决定是否应该使用CTE?

时间:2012-07-12 07:05:16

标签: sql database sql-server-2008 tsql common-table-expression

忍受我,我是中级的​​新手。

我的问题是 - 我什么时候应该使用CTE?我如何决定是否应该使用CTE?

我什么时候应该使用它:

;with cteTesting as
(
    select  *
    from    testing.first_table
)
select  *
from    testing.second_table s
        inner join cteTesting t
            on s.key = t.key

通过这个:

select  *
from    testing.second_table s
        inner join
        (
            select  *
            from    testing.first_table
        ) t
        on s.key = t.key

为什么?这只是代码流,代码可读性 - 还是有更技术性的东西?在某些情况下,会产生更好的执行计划吗?

编辑:刚刚意识到我的示例代码示例非常糟糕。我试图强调,在很多情况下我可以在from语句中使用select而不是CTE - 我如何决定应该使用哪一个?

4 个答案:

答案 0 :(得分:5)

对于简单的例子,它没有太大的区别。如果您需要使用Recursive功能来构建层次结构,那么您没有太多选择 - 您需要使用CTE。

另一种情况是,它可能不会产生很大的性能差异,但是为了提高可读性,就是需要多次连接相同的子查询时。如果您正在使用子查询,则必须重复整个表达式,而对于CTE,您只需使用该名称两次:

;With NamedExpression as (
    select t1.ID,t2.ID as ID2,SUM(t3.Value) as Val
    from
      Table1 t1
        left join
      Table2 t2 on t1.id = t2.t1id
         inner join
      Table3 t3 on t3.col = t1.id or t3.col2 = t2.id
    group by
      t1.ID,t2.ID
)
select
    *
from
    NamedExpression ne
        inner join
    NamedExpression ne2
        on
            ne.ID2 = ne2.ID

还应该注意的是,如果你做上面的子查询,并且表达式特别复杂,读者/维护者有时需要时间来验证这两个子查询实际上是相同的,并且没有一些微妙的两者之间的差异


此外,如果你有一个缩进样式,说子查询应该显示在右边而不是它们的封闭查询,那么构建在其他表达式上的表达式可以导致所有代码向右移动 - 而对于CTE,你在构建每个子表达式(CTE)时停止并向左移动:

;WITH CTE1 AS (
    SELECT
    ...
), CTE2 as (
    SELECT
    ...
    FROM CTE1
), CTE3 as (
    SELECT
    ...
    FROM CTE2
)
select * from CTE3

VS

select *
from
   (
        select ...
        from
             (
                 select ...
                 from
                     (
                          select ...

答案 1 :(得分:4)

个人发现CTE版本更易读,特别是如果选择变大。

当您在主SELECT中多次使用派生表时,最好使用CTE,因为它告诉数据库您只想运行一次。如果优化器足够聪明,可以在from子句中检测两个相同的子选择并且只运行一次,我就不会感到惊讶:

with foo as (
   select ..
   from bar
)
select f1.*
from foo f1 
  join foo f2 on ...

VS

select f1.*
from (select ... from bar ) f1
  join (select ... from bar) f2 on ...

我认为最重要的部分是保持一致(在你的写作和团队内部)。

答案 2 :(得分:3)

我注意到JOIN(特别是当与很多WHERE子句结合使用时)在涉及大数据集时可能会带来灾难性的表现。

CTE可以通过仅选择相关记录并加入这些子集来解决此问题。

将CTE视为一种预先选择,为最终的SELECT准备数据。

答案 3 :(得分:2)

我使用CTE的另一个原因并不是要替换派生表,而是要确保复杂的报告SQL包含正确的记录。因此,假设您正在执行某种类型的财务报告,并且您希望确保准确返回所需的记录。如果有10个连接,则很难判断数据是否正确。

因此,我使用CTE构建了一个复杂的查询。因此,例如我只想要符合某些标准的订单。第一个CTE是挑选出来的那个。我写它然后在CTE上运行一个选择。这告诉我我的基线订单数量,这样当我添加复杂性时,我可以立即看到数字的变化位置,并确定它是否应该更改或者我是否需要更改查询。如果我需要左连接或内部连接,或者我可能需要在相关表上的条件将其限制为一条记录,这可以让我快速了解。

通常当我这样做时,在进入最终选择之前我将链接CTES更简单。而这一点的另一个价值是,我发现在更改它们时,更容易维护这些复杂的报告查询。因此,假设我有一个链中的CTE,如:

  • 订单
  • 费用摘要
  • 客户人口统计

然后,当我需要更改有关如何进行成本计算的内容时,更容易找到进行更改的位置并更容易检查最终结果。