2015-11-24 37 views
0

我正在爲每小時運行一次的ETL腳本創建一個存儲過程,以向用戶提供特定操作的結果。加速更新語句T-SQL with Top 1

我需要找到當前結果的前一個結果。這很好,我有一個工作查詢,我可以導出到Excel中。但是現在我想讓這個過程自動化。

存儲過程平均每次運行42秒。在服務器上每小時運行一次時,這是不可行的,因爲我還有其他自動化腳本也在運行。

我的問題是28秒內存儲過程平均值的一部分,而其他部分通常不到一秒鐘(在SSMS中00:00:00出現)。

我已經設法減少其他塊的運行時間,我自己把它平均減少到42秒,但我不能這樣做。

我想知道你們中的任何人是否知道任何特定的方法來加速這個小塊?

UPDATE #tmp 
SET prev_test_date = ( 
    SELECT TOP 1 r.test_date 
    FROM [dbo].[results] r (NOLOCK) 
    WHERE r.number = #tmp.number 
    AND r.test_date < #tmp.test_date 
    ORDER BY r.test_date DESC 
)  

我本來打算用由於查詢的TOP 1份加入該加快步伐,雖然我不能做到這一點。

任何想法?

+1

我有一種感覺,你扔在那裏的NOLOCK提示「加快速度」。該查詢提示不是一個快速按鈕。它有一些非常嚴重的副作用。在使用之前,您需要充分理解該提示。 http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/ –

+0

我添加了它,因爲我們不介意弄髒結果,它會加快速度。 – Paul

+2

這遠不止是髒讀。在你把這個提示潑到整個地方之前,你真的應該閱讀那篇文章。有時間是合適的,這可能是其中之一,但很多人認爲這只是髒讀。 –

回答

2

對於此查詢:

UPDATE #tmp 
SET prev_test_date = ( 
    SELECT TOP 1 r.test_date 
    FROM [dbo].[results] r 
    WHERE r.number = #tmp.number AND 
      r.test_date < #tmp.test_date 
    ORDER BY r.test_date DESC 
) 

你想在r(number, test_date)的索引。

如果您正在使用SQL Server 2012+和測試日期不被複制,你也可以寫爲:

with r as (
     select r.*, 
      lag(r.test_date) over (partition by r.number order by r.test_date desc) as prev_test_date 
     from [dbo].[results] r 
    ) 
update t 
    set t.prev_test_date = r.prev_test_date 
    from #tmp t join 
     r 
     on t.number = r.number; 

事實上,如果是這樣的話,你可能不需要臨時表。您可能只需要使用lag()即可修改代碼。

+0

lag()做什麼? – Paul

+0

@Paul。 。 。 https://msdn.microsoft.com/en-us/library/hh231256.aspx。 –

+0

嗨,很抱歉,我在SQL Server 2012上。這僅適用於Azure,2014和2016. – Paul

1
UPDATE #tmp 
    SET #tmp.prev_test_date = tt.maxdate 
from #tmp 
join 
(
select #tmp.number, max(r.test_date) maxdate 
    from #tmp 
    join [dbo].[results] r (NOLOCK) 
     on r.number = #tmp.number 
     AND r.test_date < #tmp.test_date 
group by #tmp.number 
) tt 
on tt.number = #tmp.number 

,並有兩個#tmp和數量[結果]指標,text_date

+0

Msg 157,Level 15,State 1,Line 53 集合可能不會出現在UPDATE語句的集合列表中。 – Paul

+0

@保羅是的,我忘了。需要有一個子查詢。我會修復它或明天刪除 – Paparazzi

1
UPDATE #tmp 
SET prev_test_date = ( 
    SELECT max(r.test_date) 
    FROM [dbo].[results] r (NOLOCK) 
    WHERE r.number = #tmp.number 
    AND r.test_date < #tmp.test_date  
) 

沒有更多的信息很難說,但如果有實在太高了處理您可能需要進行單獨的預先計算的表,並在數據更改時逐步更新。

+0

不知道這是否會更快,但肯定值得一試 – Paparazzi

+0

此解決方案的處理時間與原始處理時間相同。抱歉。 D: – Paul

+0

好的,我甚至沒有想到加快速度,它的目標是「由於查詢的TOP 1部分,我無法做到這一點」 - 現在沒有TOP 1部分。在16M行和300K獨特的「數字」表上進行測試,該查詢大約需要7秒鐘。 –

0

我不得不作出關於結構和你的表的內容有一些假設,但如果我的假設是正確的,這裏的方法我通常在這種情況下使用:

with cteOrderedResults as (
    -- Ideally R will be clustered by number, test_date for this 
    select  R.number 
       ,R.test_date 
       ,row_number() over ( partition by R.number 
             order by R.test_date desc 
             -- So the most recent R.test_date from 
             -- before T.test_date gets RowNo=1 
           ) as RowNo 
    from  dbo.results R 
    inner join #tmp T   on R.number=T.number 
           and R.test_date<T.test_date 

) 
update  T 
set   T.prev_test_date=R.test_date 
from  #tmp T 
inner join cteOrderedResults R on T.number=R.number 
            and 1=R.RowNo 

這種方法適用迅速給我在行程上達到了大約百萬大關。正如我所評論的,我相信分區row_number()將會利用相應的聚集索引(如果存在的話)。如果您沒有適當地聚集表,您可能會發現這種方法無法如此快速地工作。

我同意在這裏其他地方提出的意見,如果您確實需要,您應該只添加nolock提示。如果你這樣做,你應該使用完全正確的語法,with (nolock)。從official MSDN page

省略WITH關鍵字是一個不推薦的功能:此功能將在未來版本的Microsoft SQL Server中刪除。