考虑以下问题......
SELECT
*
,CAST(
(CurrentSampleDateTime - PreviousSampleDateTime) AS FLOAT
) * 24.0 * 60.0 AS DeltaMinutes
FROM
(
SELECT
C.SampleDateTime AS CurrentSampleDateTime
,C.Location
,C.CurrentValue
,(
SELECT TOP 1
Previous.SampleDateTime
FROM Samples AS Previous
WHERE
Previous.Location = C.Location
AND Previous.SampleDateTime < C.SampleDateTime
ORDER BY Previous.SampleDateTime DESC
) AS PreviousSampleDateTime
FROM Samples AS C
) AS TempResults
假设所有事情都是平等的,例如索引等,这是实现上述结果的最有效方法吗?那是使用SubQuery来检索最后一条记录吗?
创建一个按Location,SampleDateTime排序并为CurrentSampleDateTime和PreviousSampleDateTime设置变量的游标会更好吗...在while循环的底部设置Previous到Current?
我对CTE的不太满意这是用CTE可以更有效地完成的事情吗?如果是这样的话会是什么样的?
我可能需要检索PreviousValue和Previous SampleDateTime以获得两者的平均值。这会改变结果吗。
长话短说如果您需要在当前记录的计算中使用这些值,那么保持前一记录值的最佳/最有效方法是什么?
---- UPDATE 我应该注意到我在Location,SampleDateTime,CurrentValue上有一个聚集索引,所以可能这对结果的影响最大。
有5,591,571条记录我的查询(上面的一条)平均需要3分20秒
Joachim Isaksson平均下面的CTE需要5分15秒。
也许它需要更长的时间,因为它没有使用聚集索引,而是使用rownumber进行连接?
我开始测试光标方法,但它已经在10分钟了......所以不要继续那个。
我会给它一天左右的时间,但我想我会接受Joachim Isaksson提供的CTE答案,因为我找到了获得最后一行的新方法。
有人可以同意,它是使Location子查询方法更快的Location,SampleDateTime,CurrentValue的索引吗?
我没有SQL Server 2012,因此无法测试LEAD / LAG方法。我敢打赌,这比我尝试的任何事情都要快,因为微软有效地实施了这一点。可能只需将指针交换到每行末尾的内存引用。
答案 0 :(得分:1)
与往常一样,使用您的真实数据进行测试是最重要的。
这是一个CTE版本,显示每个位置的样本,其中包含前一个样本的时间增量。它使用OVER
排名,与子查询相比,它通常可以很好地解决同样的问题。
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Location
ORDER BY SampleDateTime DESC) rn
FROM Samples
)
SELECT a.*,CAST((a.SampleDateTime - b.SampleDateTime) AS FLOAT)
* 24.0 * 60.0 AS DeltaMinutes
FROM cte a
LEFT JOIN cte b ON a.Location = b.Location AND b.rn = a.rn +1
答案 1 :(得分:1)
如果您使用的是SQL Server 2012,则可以使用LAG窗口函数来检索上一行中指定列的值。如果没有前一行,则返回null。
SELECT
a.*,
CAST((a.SampleDateTime - LAG(a.SampleDateTime) OVER(PARTITION BY a.location ORDER BY a.SampleDateTime ASC)) AS FLOAT)
* 24.0 * 60.0 AS DeltaMinutes
FROM samples a
ORDER BY
a.location,
a.SampleDateTime
你必须运行一些测试才能看出它是否更快。如果你没有使用SQL Server 2012那么至少这可能让其他人知道如何用2012完成它。我喜欢@Joachim Isaksson的答案,使用带有Row_Number()/ Partition By的CTE 2008和2005
您是否考虑过创建临时表来代替CTE或子查询?您可以在临时表上创建更适合RowNumber上的连接的索引。
CREATE TABLE #tmp (
RowNumber INT,
Location INT,
SampleDateTime DATETIME,
CurrentValue INT)
;
INSERT INTO #tmp
SELECT
ROW_NUMBER() OVER (PARTITION BY Location
ORDER BY SampleDateTime DESC) rn,
Location,
SampleDateTime,
CurrentValue
FROM Samples
;
CREATE INDEX idx_location_row ON #tmp(Location,RowNumber) INCLUDE (SampleDateTime,CurrentValue);
SELECT
a.Location,
a.SampleDateTime,
a.CurrentValue,
CAST((a.SampleDateTime - b.SampleDateTime) AS FLOAT) * 24.0 * 60.0 AS DeltaMinutes
FROM #tmp a
LEFT JOIN #tmp b ON
a.Location = b.Location
AND b.RowNumber = a.RowNumber +1
ORDER BY
a.Location,
a.SampleDateTime