5

请耐心等待,我是中级新手。

我的问题是 - 我应该什么时候使用 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

4 回答 4

5

对于简单的示例,它没有太大区别。如果您需要使用递归功能来构建层次结构,那么您别无选择 - 您需要使用 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

与:

select *
from
   (
        select ...
        from
             (
                 select ...
                 from
                     (
                          select ...
于 2012-07-12T07:18:54.777 回答
4

个人发现 CTE 版本更具可读性,尤其是在选择变大的情况下。

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

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

对比

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

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

于 2012-07-12T07:17:28.277 回答
3

我注意到当涉及大型数据集时,JOIN(尤其是与许多 WHERE 子句结合使用时)可能会产生灾难性的性能。

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

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

于 2012-07-12T07:41:17.717 回答
2

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

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

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

  • 订单
  • 成本汇总
  • 客户统计

然后,当我需要更改有关如何进行成本计算的某些内容时,既更容易找到在哪里进行更改,也更容易检查最终结果。

于 2012-07-12T14:55:49.027 回答