2017-05-30 296 views
1

我想刪除SQL中的遊標,以提高性能(並且因爲我想學習如何使用最佳實踐,並且最佳實踐應該基於,無需光標進行設置)。刪除cursur SQL語句

無論如何,我有一個臨時表看起來像這樣:

+------------+--------+-------+----+ 
|  Period | Change | Value | NR | 
+------------+--------+-------+----+ 
|  201705 |  7 | 26055 | 1 | 
|  201704 |  29 |  0 | 2 | 
|  201703 | -92 |  0 | 3 | 
|  201702 | -338 |  0 | 4 | 
|  201701 |  81 |  0 | 5 | 
|  201612 | 107 |  0 | 6 | 
|  201611 |  72 |  0 | 7 | 
|  201610 |  54 |  0 | 8 | 
|  201609 |  64 |  0 | 9 | 
|  201608 |  47 |  0 | 10 | 
|  201607 |  23 |  0 | 11 | 
|  201606 |  45 |  0 | 12 | 
+------------+--------+-------+----+ 

目前,光標的動作如下:

DECLARE @Value INT  
BEGIN  
DECLARE c_Value CURSOR FOR 
    SELECT NR 
    FROM ##TMP 
    WHERE Value = 0  
----    
OPEN c_Value   
FETCH NEXT FROM c_Value 
INTO @Value 

WHILE @@FETCH_STATUS = 0 
BEGIN   
    SELECT @Value = Value - Change 
    FROM ##TMP 
    WHERE NR = (Select MAX(NR) From ##TMP WHERE Value <> 0)     
        BEGIN     
         UPDATE ##TMP 
         SET Value = @Value 
         WHERE NR = (Select MAX(NR)+1 From ##TMP WHERE Value <> 0) 
        END      
     FETCH NEXT FROM c_Value 
      INTO @Value    
      END 

    CLOSE c_Value 
    DEALLOCATE c_Value  
END 

結果:

+------------+--------+-------+----+ 
|  Period | Change | Value | NR | 
+------------+--------+-------+----+ 
|  201705 |  7 | 26055 | 1 | 
|  201704 |  29 | 26048 | 2 | 
|  201703 | -92 | 26019 | 3 | 
|  201702 | -338 | 26111 | 4 | 
|  201701 |  81 | 26449 | 5 | 
|  201612 | 107 | 26368 | 6 | 
|  201611 |  72 | 26261 | 7 | 
|  201610 |  54 | 26189 | 8 | 
|  201609 |  64 | 26135 | 9 | 
|  201608 |  47 | 26071 | 10 | 
|  201607 |  23 | 26024 | 11 | 
|  201606 |  45 | 26001 | 12 | 
+------------+--------+-------+----+ 

那麼,如何我可以實現這個結果,而不使用遊標?我用CTE嘗試過,但我無法得到這個結果。

+0

這不能由一組操作來解決,但你可以使用相關子查詢,窗口函數或遞歸CTE來解決它。請向我們展示您嘗試過的CTE,我們可以從那裏建立。 – Jayvee

+1

什麼是sql版本? –

+0

你應該看看這篇文章:https://msdn.microsoft.com/en-us/library/ms189461(v=sql.110).aspx – tgr

回答

1

首先你需要得到初始值。

SELECT [Value] as StartValue 
FROM Table1 
WHERE NR = 1 

然後,使用累計SUM()可以修改初始值,注意你必須忽略[Change]值的每一行

SQL DEMO

WITH CTE as (
    SELECT [Value] as StartValue 
    FROM Table1 
    WHERE NR = 1 
)  
SELECT T.*, 
     - SUM(CHANGE) OVER (ORDER BY [NR]) 
     + [CHANGE] as TotalChange, -- just for debug, dont need this.  
     CTE.StartValue 
     - SUM([CHANGE]) OVER (ORDER BY [NR]) 
     + [CHANGE] as NewValue 
FROM Table1 T 
CROSS JOIN CTE 

輸出

enter image description here

+0

謝謝,我很喜歡這種方法。我使用2008並收到錯誤消息:並行數據倉庫(PDW)功能未啓用。我會盡量修改一下。 –

0

這可以通過使用SQL Server 2014

select period, 
change, 
NR = Row_Number() Over(Order by period), 
Value = Sum(Change) Over(Order by period rows unbounded preceding) 

這是寫意,不得解析推出了窗口函數來解決,而應該讓你足夠接近。

+1

'NR'不會改變。如果你檢查我的答案,你需要使用'Sum(change)'來影響起始值。 –

1

的SQL Server 2012或更高版本:

CREATE TABLE ##TMP (
    Period int 
,Change float 
,Value float 
,Nr int 
); 
INSERT INTO ##TMP VALUES 
(201705,  7 , 26055, 1) 
,(201704, 29 ,  0, 2) 
,(201703, -92 ,  0, 3) 
,(201702, -338 ,  0, 4) 
,(201701, 81 ,  0, 5) 
,(201612, 107 ,  0, 6) 
,(201611, 72 ,  0, 7) 
,(201610, 54 ,  0, 8) 
,(201609, 64 ,  0, 9) 
,(201608, 47 ,  0,10) 
,(201607, 23 ,  0,11) 
,(201606, 45 ,  0,12) 

;with cte as (
SELECT Period, Change, value as Value_Org, Nr, SUM(Value - Change) OVER (ORDER BY Nr ASC) as Value 
FROM ##TMP 
) 
select a.Period, a.Change, a.nr, a.value_org, a.value, b.value, 
isnull(b.value, a.value_org) 
from cte as a 
left outer join cte as b 
on a.nr = b.nr+1 
order by a.Nr