SQL:使用在后续计算中刚刚定义的计算列

SQL:使用在后续计算中刚刚定义的计算列,sql,sql-server,Sql,Sql Server,标题看起来有点笨重,但我不知道有什么更好的词来形容我想要达到的阶段。这是一个非常简单的问题:如果我在SQL查询中定义了一个列,其中为关键字,那么我可以立即从下一列访问该列吗 SELECT LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS LeftOn, LeftOn - StoredOn AS TimeDifference FROM MyTable 很明显,这个不起作用。我知道,我可以通过使

标题看起来有点笨重,但我不知道有什么更好的词来形容我想要达到的阶段。这是一个非常简单的问题:如果我在SQL查询中定义了一个列,其中
关键字,那么我可以立即从下一列访问该列吗

SELECT
  LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS LeftOn,
  LeftOn - StoredOn AS TimeDifference
FROM MyTable

很明显,这个不起作用。我知道,我可以通过使用完全相同的
LEAD
函数替换第二列定义中的
LeftOn
来实现这一点。但是这是否会影响性能,或者SQL server是否理解这两个调用是冗余的?

使用
子查询

select t1.LeftOn, t1.LeftOn  - t1.StoredOn  AS TimeDifference from
(SELECT
  LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS LeftOn,
  StoredOn
FROM MyTable) t1

使用CTE函数即通用表表达式

WITH CTE AS 
(
SELECT LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS LeftOn,ID_ChargeCarrier AS CTEId FROM MyTable
)

SELECT   LeftOn - a.StoredOn AS TimeDifference
FROM MyTable a INNER JOIN CTE b
ON a.ID_ChargeCarrier = b.CTEId

如果您想要获得性能,您只需在此处重复
LEAD

SELECT
  LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS LeftOn,
  LEAD(StoredOn) OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) - StoredOn AS TimeDifference
FROM MyTable;

的确,这看起来不太好,但它避免了子查询,这意味着如果索引存在且可用,上述查询可以完全使用索引运行。

我做了一些测试,发现不同解决方案的性能受涉及的记录数的影响,因此可以呈现不同的场景

CTE
SUBSELECT
实际上几乎相同,也是相同的计划。。
Double LEAD()
应该是最差的,从计划上看,
LEAD()
是两次计算的(使用段和序列项目),成本大于cte和subselect的标量值。 但是,
Double LEAD()
似乎可以更好地利用并行性,因此总执行时间可能会更低

对于行数非常多的大型表,
Double LEAD()
可能是最差的选择,而
CTE
SUBSELECT
将是最好的选择

还有另一个选项,您可以将
CTE
ROW\u NUMBER()
一起使用,而不是
LEAD()
。 与其他查询计划相比,查询计划确实难看,但执行时间可能比其他解决方案要好,它取决于SQL Server版本(Express或Standard)和服务器硬件(cores和ram)的并行性

检查类似的内容:

;WITH
l AS (
    SELECT ID_ChargeCarrier, StoredOn, ROW_NUMBER() OVER (PARTITION BY ID_ChargeCarrier ORDER BY StoredOn) AS row_id
    FROM MyTable
)
SELECT 
    ID_ChargeCarrier, l1.StoredOn, l2.StoredOn AS LeftOn,  
    l2.StoredOn, l1.StoredOn AS TimeDifference
FROM l l1
LEFT JOIN l l2 ON l1.ID_ChargeCarrier = l2.ID_ChargeCarrier AND l1.n = l2.n - 1
对于我的测试,我使用这个表格,填充了300万条记录

CREATE TABLE [dbo].[_Memberships](
    [MembershipId] [int] NOT NULL,
    [ValidFromDateKey] [date] NOT NULL,
    [ValidToDateKey] [date] NULL,
    [ColInt] [int] IDENTITY(1,1) NOT NULL,
    [ColGUID] [uniqueidentifier] NULL,
    [ColVarChar] [varchar](250) NULL,
    [ColChk]  AS (checksum([ColVarChar])),
    CONSTRAINT [PK_Memberships] PRIMARY KEY CLUSTERED ([MembershipId] ASC) 
        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[_Memberships] ADD  CONSTRAINT [DF_Memberships_ColGUID]  DEFAULT (newid()) FOR [ColGUID]
GO

CREATE NONCLUSTERED INDEX [ix_checksum] ON [dbo].[_Memberships]([ColChk] ASC)
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
我知道这个解决方案不是最好的,但我要指出,即使是这样一个(糟糕的)解决方案在特定情况下也可能是一个解决方案

我认为主要的区别可能在于SQL版本和服务器的硬件。如果您没有Express edition,并且有许多内核,那么情况可能会很快发生变化

还要注意表中数据更改的速度,以及查询是否始终相同或是否已参数化


缓存可以有一个角色。

使用
CTE
派生表
@Squirrel什么是
CTE
?我从未听说过这个术语。
公共表表达式
与只复制
LEAD
调用相比,会带来任何性能上的好处吗?好吧,您将有两个单独的计算,我已经用Tim Biegeleisen的解决方案在基准测试中测试了您的想法,它们具有完全相同的运行时间。但是,它们创建了一个稍有不同的执行计划。与Tim Biegeleisen的解决方案相比,哪一个更可取?当您可以只公开
CTE
中的
StoredOn
列时,为什么还要重新合并同一个表?我用三种不同的方法用三个视图进行了一些测试(未修改,双
导线
CTE
)与akhilesh singh的解决方案相比,
CTE
版本是最慢的,哪一个更可取?@AndréReichet你应该对两个版本进行基准测试,看看哪个更快。如果两个性能大致相同,那么可能会使用其他答案,这些答案比我的答案要简单一些。看起来,这两个版本都不合适t减少任何额外成本。
LEAD
的复制在执行计划中引入了一个新的跃点,但成本为0。SQL server似乎优化了第二个相同的调用。这或多或少是我所期望的。与调用
LEAD
SQL Server很可能会将这三个查询视为相同的。如果有疑问,您可以比较它们的计划。它们应该是相同的。您可能还需要添加用于总结基准的数据集的详细信息(例如大小、基数等)。这很有趣。在我们的生产数据库中,CTE是迄今为止性能最差的选项。此外,即使您不需要请求中的计算列,也无法优化CTE预选。