0

考虑以下查询...

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 来检索最后一条记录?

我最好创建一个按位置排序的游标,SampleDateTime 并为 CurrentSampleDateTime 和 PreviousSampleDateTime 设置变量...在 while 循环的底部将 Previous 设置为 Current?

我对 CTE 不是很好,这可以通过 CTE 更有效地完成吗?如果是这样,那会是什么样子?

我可能必须检索 PreviousValue 以及 Previous SampleDateTime 以获得两者的平均值。这是否会改变结果。

长话短说,如果您需要在当前记录的计算中使用这些值,那么保持先前记录值的最佳/最有效方法是什么?

----更新我应该注意,我在 Location、SampleDateTime、CurrentValue 上有一个聚集索引,所以这可能是影响结果的最重要因素。

有 5,591,571 条记录,我的查询(上面的那个)平均需要 3 分 20 秒

低于 Joachim Isaksson 的 CTE 平均需要 5 分 15 秒。

也许它需要更长的时间,因为它没有使用聚集索引,而是使用行号进行连接?

我开始测试游标方法,但它已经在 10 分钟......所以不要继续那个。

我会给它一天左右的时间,但我认为我会接受 Joachim Isaksson 提供的 CTE 答案,因为我找到了一种获得最后一行的新方法。

谁能同意是 Location、SampleDateTime、CurrentValue 上的索引使子查询方法更快?

我没有 SQL Server 2012,所以无法测试 LEAD/LAG 方法。我敢打赌,假设微软有效地实现了这一点,那将比我尝试过的任何事情都要快。可能只需要在每行末尾交换指向内存引用的指针。

4

2 回答 2

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

一个用于测试的 SQLfiddle

于 2013-07-30T17:19:37.650 回答
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 年.

SQL小提琴

您是否考虑过创建一个临时表来代替 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

SQL 小提琴 #2

于 2013-07-30T18:08:20.787 回答