在SQL Server中优化ROW_NUMBER()

时间:2010-06-02 17:42:42

标签: sql-server sql-server-2005 tsql optimization query-optimization

我们有许多机器以零星的间隔将数据记录到数据库中。对于每条记录,我想获得录制和之前录制之间的时间段。

我可以使用ROW_NUMBER执行此操作,如下所示:

WITH TempTable AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Machine_ID ORDER BY Date_Time) AS Ordering
    FROM dbo.DataTable
)

SELECT [Current].*, Previous.Date_Time AS PreviousDateTime
FROM TempTable AS [Current]
INNER JOIN TempTable AS Previous 
    ON [Current].Machine_ID = Previous.Machine_ID
    AND Previous.Ordering = [Current].Ordering + 1

问题是,它真的慢(在一个有大约10k条目的表上几分钟) - 我尝试在Machine_ID和Date_Time上创建单独的指示,以及一个连接索引,但没有任何帮助

无论如何都要重写这个查询以加快速度吗?

6 个答案:

答案 0 :(得分:7)

给定的ROW_NUMBER()分区和顺序要求(Machine_ID, Date_Time)上的索引在一次传递中满足:

CREATE INDEX idxMachineIDDateTime ON DataTable (Machine_ID, Date_Time);

Machine_ID和Date_Time上的单独索引几乎没有帮助。

答案 1 :(得分:6)

与此版本相比如何?:

SELECT x.*
    ,(SELECT MAX(Date_Time)
      FROM dbo.DataTable
      WHERE Machine_ID = x.Machine_ID
          AND Date_Time < x.Date_Time
    ) AS PreviousDateTime
FROM dbo.DataTable AS x

或者这个版本?:

SELECT x.*
    ,triang_join.PreviousDateTime
FROM dbo.DataTable AS x
INNER JOIN (
    SELECT l.Machine_ID, l.Date_Time, MAX(r.Date_Time) AS PreviousDateTime
    FROM dbo.DataTable AS l
    LEFT JOIN dbo.DataTable AS r
    ON l.Machine_ID = r.Machine_ID
        AND l.Date_Time > r.Date_Time
    GROUP BY l.Machine_ID, l.Date_Time
) AS triang_join
ON triang_join.Machine_ID = x.Machine_ID
    AND triang_join.Date_Time = x.Date_Time

对于Machine_ID,Date_Time的索引,两者都会表现最佳,并且对于正确的结果,我假设这是唯一的。

你没有提到隐藏在*中的东西,有时候意味着很多,因为Machine_ID,Date_Time索引通常不会覆盖,如果你有很多列,或者他们有很多数据,。 ..

答案 2 :(得分:4)

如果dbo.DataTable中的行数很大,那么由于CTE自身加入自身,您可能会遇到问题。有一篇博文详细解释了这个问题here

在这种情况下偶尔我会创建一个临时表来插入CTE查询的结果,然后对该临时表进行连接(尽管这通常适用于对temp的大量连接的情况表是必需的 - 在单个连接的情况下,性能差异将不太明显)

答案 3 :(得分:2)

我在SQL Server 2005中使用CTE时遇到了一些奇怪的性能问题。在许多情况下,用真实临时表替换CTE解决了这个问题。

在使用CTE进行任何进一步操作之前,我会尝试这个。

我从未找到任何有关我见过的性能问题的解释,并且真的没有时间深入研究根本原因。但是我总是怀疑引擎无法像优化临时表那样优化CTE(如果需要更多优化,可以对其进行索引)。

<强>更新

在您评论这是一个视图之后,我首先会使用临时表测试查询,看看它是否表现更好。

如果确实如此,并且使用存储过程不是一个选项,您可以考虑将当前CTE转换为索引/物化视图。在走这条路之前,你会想要阅读这个主题,因为这是一个好主意取决于很多因素,其中最重要的是数据的更新频率。

答案 4 :(得分:0)

如果您使用触发器存储最后一个时间戳,每次减去什么来获​​得差异怎么办?

答案 5 :(得分:0)

如果您经常需要这些数据,而不是每次提取数据时计算它,为什么不添加列并在添加行时计算/填充它?

(Remus的复合索引将使查询更快;仅运行一次应使其更快。)