SQL:使用在后续计算中刚刚定义的计算列
标题看起来有点笨重,但我不知道有什么更好的词来形容我想要达到的阶段。这是一个非常简单的问题:如果我在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 很明显,这个不起作用。我知道,我可以通过使
为
关键字,那么我可以立即从下一列访问该列吗
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预选。