6

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

如下,我可以做到这一点使用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 

的问题是,它会真的慢(几分钟桌子上有大约10,000项) - 我试图创建于Machine_ID和独立indicies Date_Time和单个联合索引,但没有任何帮助。

无论如何重写这个查询更快?

回答

5

它是如何比较,这个版本?:

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索引通常不会覆盖,如果你有很多列或者他们有很多数据, ...

+0

第二个查询以秒为单位而不是以分钟为单位完成,但第一个查询的执行速度比我的时间要快。很好,谢谢! – 2010-06-03 14:52:03

7

给定ROW_NUMBER()分区和顺序需要对(Machine_ID, Date_Time)索引在一次通过,以满足:

CREATE INDEX idxMachineIDDateTime ON DataTable (Machine_ID, Date_Time); 

上Machine_ID和DATE_TIME单独的索引将有助于小,如果有的话。

+0

正如我所说,我也创建了该索引,它并没有改善查询性能。 – 2010-06-02 20:06:33

+4

这是因为你的*触发了索引临界点。将其限制为仅包含需要使用的列,包括使非聚集索引覆盖。如果需要太多列,则必须将其更改为聚集索引,并带来所有后果。 – 2010-06-02 20:58:42

+0

您似乎是正确的,删除​​会将查询时间减少到几秒钟。我无法想象为什么会发生这种情况 - 你能提供任何链接,指出*指数转折点是什么? – 2010-06-03 14:49:46

0

如果您使用触发器来存储上次的时间戳,每次减去以获得差异会怎样?

+0

不幸的是,这是历史数据,并不总是按顺序添加。 – 2010-06-02 20:07:57

2

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

我会在继续使用CTE之前尝试一下。

我从来没有发现我见过的性能问题的任何解释,也没有任何时间深入挖掘根本原因。但是我一直怀疑引擎无法像优化临时表一样优化CTE(如果需要更多优化,可以对其进行索引)。

更新

您的评论,这是一个观点后,我会先测试用临时表的查询,看看是否能更好地执行。

如果有,并且使用存储的proc不是一个选项,那么可以考虑将当前的CTE设置为索引/物化视图。在走这条路之前,你会想要阅读这个主题,因为这是否是一个好主意取决于很多因素,而不仅仅是更新数据的频率。

+0

我该怎么做?我需要用Sproc替换视图(因为视图不能有变量)? – 2010-06-02 20:08:55

+0

是的,我不清楚这是你的问题的看法。看到我的答案的更新(将在几分钟后)。 – 2010-06-02 20:23:55

0

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

(Remus的复合索引会使查询速度快;运行它只有一次应使其更快仍然)

4

如果行中dbo.DataTable数量较大,则很可能是您所遇到的由于CTE自己加入到自己的问题。有一篇博客文章详细解释了这个问题here

有时在这种情况下,我采取了创建一个临时表来插入CTE查询的结果,然后对临时表进行连接(尽管这样做有通常被用于需要大量地加入反对临时表的情况下 - 在单个连接的性能差异的情况下,将减少noticable)

+1

我第二种方法。 CTE只是内联重写。就像重复自己的代码和自我加入一样,没有任何东西可以保证优化器将它暂存到临时表中。如果你把东西放在你自己的桌子上,你可以选择索引和/或避免双重工作。话虽如此,我确实使用CTE,其中代码维护非常重要,并且模式可能会很快发生变化(或者在视图中,像这种情况一样)。 – 2010-06-02 20:49:36