2015-02-12 2 views
0

Мне задали вопрос и попытались выяснить способ удаления курсора, но сохраняя функциональность, потому что стартовая таблица может попасть в миллионы строк.Попытка избежать использования курсора

Пример данных в таблице:

ID DollarValue Month  RowNumber 
1  $10   1/1/2014 1 
1  $15   2/1/2014 2 
1 -$40   3/1/2014 3 
1  $50   4/1/2014 4 
2 -$11   1/1/2014 1 
2  $11   2/1/2014 2 
2  $5   3/1/2014 3 

Ожидаемые результаты:

ID DollarValue Month  RowNumber TestVal 
1  $10   1/1/2014 1   1 
1  $15   2/1/2014 2   0 
1 -$40   3/1/2014 3   -1 
1  $50   4/1/2014 4   1 
2 -$11   1/1/2014 1   -1 
2  $11   2/1/2014 2   0 
2  $5   3/1/2014 3   1 

Вот логика (псевдокод), что происходит внутри курсора:

If a @ID <> @LastId AND @Month <> @LastMonth 
    Set @RunningTotal = @DollarValue 
    Set @LastMonth = '12/31/2099' 
    Set @LastID = @ID 
    Set @TestVal = Sign(@DollarValue) 
Else 
    If Sign(@RunningTotal) = Sign(@RunningTotal + @DollarValue) 
    Set @TestVal = 0 
Else 
    Set @TestVal = Sign(@DollarValue) 

Set @RunningTotal = @RunningTotal + @DollarValue 

Любая идея, как Я могу изменить это, чтобы установить на основе?

+0

Вы смотрели в рекурсивных КТР? –

+1

Что это в конечном итоге делает с '@ RunningTotal' и' @ TestVal'? Он пишет в другую таблицу? Что-то обновить? Было бы легче придумать эквивалент, если бы мы знали, какова конечная цель. –

+0

http://stackoverflow.com/questions/11981979/t-sql-using-sum-for-a-running-total – HLGEM

ответ

2

Вы можете использовать оконную версию SUM для расчета текущих итогов:

;WITH CTE AS (
    SELECT ID, DollarValue, Month, RowNumber, 
      SUM (DollarValue) OVER (PARTITION BY ID ORDER BY RowNumber) as RunningTotal 
    FROM #mytable 
) 
SELECT C1.ID, C1.DollarValue, C1.Month, C1.RowNumber, 
     CASE WHEN C1.RowNumber = 1 THEN SIGN(C1.DollarValue) 
      WHEN SIGN(C1.RunningTotal) = SIGN(C2.RunningTotal) THEN 0 
      ELSE SIGN(C1.RunningTotal) 
     END AS TestVal   
FROM CTE AS C1 
LEFT JOIN CTE AS C2 ON C1.ID = C2.ID AND C1.RowNumber = C2.RowNumber + 1 

Использования LEFT JOIN на RowNumber вы можете получить предыдущую запись и сравнить текущее нарастающий итог с предыдущим. Затем используйте простой CASE, чтобы применить правила, относящиеся к изменениям в SIGN от общего числа.

SQL FIDDLE Demo

P.S. Похоже, что вышеупомянутое решение не будет работать в версиях до SQL Server 2012. В этом случае текущий общий расчет внутри CTE должен быть заменен на «обычную» версию.

2

Это решение 2008

WITH CTE AS (
SELECT 
    AA.[ID] 
,AA.[Month] 
,AA.[RowNumber] 
,AA.[DollarValue] 
,SIGN(SUM(BB.[DollarValue])) AS RunTotalSign 

FROM YourTable AS AA 
LEFT JOIN YourTable AS BB 
     ON (AA.[ID] = BB.[ID] AND BB.[RowNumber] <= AA.[RowNumber]) 
GROUP BY AA.[ID],AA.[Month],AA.[DollarValue],AA.[RowNumber]) 
) 

SELECT 
    AA.[ID] 
,AA.[Month] 
,AA.[RowNumber] 
,AA.[DollarValue] 
,CASE WHEN AA.RunTotalSign = CC.RunTotalSign Then 0 
     ELSE AA.RunTotalSign 
     END 
    AS TestVal 
FROM CTE AS AA 
LEFT JOIN CTE AS CC 
     ON (AA.[ID] = CC.[ID] AND AA.[RowNumber] = CC.[RowNumber]+1) 
Смежные вопросы