我今天在 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 是一种声明性语言,而不是过程性语言。当您使用 CTE 作为声明所需内容的一部分时,您并不是在编写过程来获得结果。在您的查询的情况下,服务器的查询规划器会注意到您用于过滤最终结果的
date_start
和 date_expire
列引用回原始表中的列。它过滤原始表,而不是 CTE 生成的结果。
查询规划器优化软件是数千名非常聪明的程序员数十年工作的成果。它在找出访问表的巧妙方法方面做了一些令人惊奇的事情。
对于我们许多人来说,SQL 是我们遇到的第一种声明性语言。我们习惯于通过告诉计算机如何获得结果来描述我们想要的结果。但我们需要对 SQL 有不同的思维方式。
对您的问题的评论讨论了如何理解查询计划对特定查询的作用。如果查询对于您的应用程序来说太慢,您可以深入研究该计划并找出如何说服您的服务器使用更快的计划。这通常涉及添加索引。
阅读 Marcus Winand 的电子书 https://use-the-index-luke.com/ 扩展您对这一切的理解。