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