为什么我的 CTE 比自身的一部分快?

问题描述 投票:0回答:1

我今天在 SQL Server 中创建了一个 CTE,这确实让我感到困惑。它的效果非常好,但我不明白如何实现。我无法提供确切的查询,但这是一个近似值:

with a as (

    select
         dt.[Date]
        ,p.product_no
        ,[date_start] = cast( RIGHT( MIN( RIGHT( '000000' + cast( p.price as varchar ), 6) + convert( varchar, p.date_start, 112 ) ), 8 ) as date )
        ,[date_expire] = cast( RIGHT( MIN( RIGHT( '000000' + cast( p.price as varchar ), 6) + convert( varchar, isnull(p.date_expire,'1/1/9999'), 112 ) ), 8 ) as date )
        ,[price] = MIN(p.price)
    from pricing p with (nolock) 
        inner join datetable dt with (nolock) on dt.[Date] between p.date_start and isnull(p.date_expire,getDate())
    group by 
         dt.[Date]
        ,p.product_no

), b as (

    select distinct
         a.date
        ,a.product_no
        ,[date_start] = iif( isnull(LAG(a.price) OVER (PARTITION BY a.product_no ORDER BY a.date),-1) <> a.price, a.date, null )
        ,[date_expire] = iif( isnull(LEAD(a.price) OVER (PARTITION BY a.product_no ORDER BY a.date),-1) <> a.price, a.date, null )
        ,a.price
    from a

)

select distinct
     b.product_no
    ,[date_start] = coalesce( b.date_start, LAG(b.date_start) OVER (PARTITION BY b.product_no ORDER BY b.date) )
    ,[date_expire] = coalesce( b.date_expire, nullif(LEAD(b.date_expire) OVER (PARTITION BY b.product_no ORDER BY b.date), cast( getDate() as date ) ) )
    ,b.price
from b
where b.date_start is not null
   or ( b.date_expire is not null 
        and b.date_expire <> cast( getDate() as date ) )
order by 1,2;

此定价表包括 12 年左右的数百种产品的价格,并且很多时候,有多个价格同时有效。这些可能完全或部分重叠。我想做的是创建一个干净的表格,显示任何给定时间点的最低价格。看来我成功了

我不明白的是:如果你运行

a
的定义,第一个选择,它会产生数百万行。事实上,太多了,我从来没有让它完全通过。显然,它会花费很长时间并消耗大量内存。如果我运行整个事情呢?这大约需要 25 秒,并且如您所料,仅生成几千行。

那么 SQL Server 是如何实现这一目标的呢?显然,它并没有完整执行每个步骤,然后继续下一步。我想了解这个巫毒。如果这能在某种程度上帮助我理解 CTE 何时比一系列临时表更快,反之亦然,那就太好了。预先感谢!

sql sql-server query-optimization common-table-expression
1个回答
1
投票

SQL 是一种声明性语言,而不是过程性语言。当您使用 CTE 作为声明所需内容的一部分时,您并不是在编写过程来获得结果。在您的查询的情况下,服务器的查询规划器会注意到您用于过滤最终结果的

date_start
date_expire
列引用回原始表中的列。它过滤原始表,而不是 CTE 生成的结果。

查询规划器优化软件是数千名非常聪明的程序员数十年工作的成果。它在找出访问表的巧妙方法方面做了一些令人惊奇的事情。

对于我们许多人来说,SQL 是我们遇到的第一种声明性语言。我们习惯于通过告诉计算机如何获得结果来描述我们想要的结果。但我们需要对 SQL 有不同的思维方式。

对您的问题的评论讨论了如何理解查询计划对特定查询的作用。如果查询对于您的应用程序来说太慢,您可以深入研究该计划并找出如何说服您的服务器使用更快的计划。这通常涉及添加索引。

阅读 Marcus Winand 的电子书 https://use-the-index-luke.com/ 扩展您对这一切的理解。

© www.soinside.com 2019 - 2024. All rights reserved.