2013-12-18 3 views
0

У меня есть требование ниже. Как это сделать упрощенно.расчет инфляции в MS SQL

Существует таблица уровень инфляции ниже

2009, 5% 
2010, 5% 
2011, 5% 
2012, 5% 
2013, 5% 

мне нужно сначала получить

2009, 5%, 5 
2010, 5%, 5.25  - which is 5% over 5 
2011, 5%, 5.5125  - which is 5% over 5.25 
2012, 5%, 5.788125 - which is 5% over 5.5125 
2013, 5%, 6.07753125 - which is 5% over 5.788125 

А затем получить

2009, 5%, 5   , 5 
2010, 5%, 5.25  , 10.25   = 5.25 + 5 
2011, 5%, 5.5125  , 15.7625   = 5.5125 + 10.25 and so on 
2012, 5%, 5.788125 , 21.550625 
2013, 5%, 6.07753125 , 27.62815625 
+0

Версия SQL Server? Также сколько строк в реальной таблице? –

+0

SQL 2008 R2 или выше – Nagendra

+0

возможно максимум 20 строк – Nagendra

ответ

2

Я думаю, что лучший способ сделать это на SQLserver - использовать общее табличное выражение с рекурсией. Я не уверен на 100%, потому что я не могу проверить его здесь, но что-то в этом роде в качестве примера ниже. Btw Я предполагаю, что ставки в таблице хранятся в виде дроби, поэтому 5% составляет 1,05 и 10,25% является 1,1025 и т.д.

WITH MyCompoundRates (TheYear, TheRate, CompoundRate) 
AS 
(
-- select one anchor record, starting point record 
    SELECT 
     TheYear, 
     TheRate, -- I'm assuming "5%" is stored as value 1.05 
     TheRate as CompoundRate 
    FROM 
     MyRatesTable 
    WHERE 
     TheYear = 2009 -- <- starting point for recursion 
    UNION ALL 
-- select recursive records, by linking them to a previous record 
    SELECT 
     r.TheYear, 
     r.TheRate, 
     r.TheRate * c.CompoundRate as CompoundRate -- calculate compound rate 
    FROM 
     MyRatesTable r 
     JOIN MyCompoundRates c ON r.TheYear = c.TheYear+1 -- recursion! link a year to previous year 
) 
-- Statement that executes the CTE 
SELECT 
     TheYear, 
     TheRate, 
     CompoundRate 
FROM 
    MyCompoundRates 
+0

Что касается тестирования, вы можете использовать SQL Fiddle –

+0

[SQL Fiddle] (http: // sqlfiddle.com/#!6/ee62e/4/0) Не знал об этом, это похоже на rubular, за исключением SQL: D Спасибо, приятно найти. – BdR

+0

Ребята, большое спасибо за такой быстрый ответ! Мне еще предстоит пройти через ответы и обновить его завтра. – Nagendra

1

Вы можете использовать рекурсивный КТР для этого SQL Fiddle ,

WITH RecursiveCTE 
AS  (
     SELECT TOP 1 [Year], 
        [Inflation], 
        CAST(1 AS numeric(38,37)) AS Compound, 
        CAST(5 AS numeric(38,37)) AS Value, CAST(5 AS numeric(38,10)) AS Total 
     FROM Rates 
     ORDER BY [Year] 
     UNION ALL 
     SELECT R.[Year], R.[Inflation], R.Compound, R.Value, R.Total 
     FROM (
       SELECT T.*, 
         CAST(1 + R.[Inflation] * Compound AS numeric(38,37)) AS Compound, 
         CAST(R.Value * (1 + T.Inflation) AS numeric(38,37)) AS Value, 
         CAST(R.Value * (1 + T.Inflation) AS numeric(38,37)) + R.Total AS Total, 
         rn = ROW_NUMBER() OVER (ORDER BY T.[Year]) 
       FROM Rates T 
       JOIN RecursiveCTE R 
         ON R.[Year] < T.[Year] 
       ) R 
     WHERE R.rn = 1 
     ) 
SELECT [Year], [Inflation], Value, Total 
FROM RecursiveCTE; 
+0

Я не думал использовать подзапрос в рекурсивном CTE. Мне это очень нравится! – David

+0

@ Давид - На самом деле это не нужно! Я очистил запрошенный итоговый запрос, где колонка заказа не была гарантирована последовательной и повторно использовала ее здесь. В этом случае соединение на 'year = year + 1' за ответ BdR, по-видимому, будет прекрасным. –

+0

Ребята, большое спасибо за такой быстрый ответ !! Мне еще предстоит пройти через ответы и обновить его завтра. – Nagendra

1

Я думаю, что все было весело играть с этим, так как куча ответов пришли на в то же время.

DECLARE @Inflation TABLE (
    YearNum INT 
    ,Inflation FLOAT 
    ,LowerCalc INT 
    ,AdjustedInflation FLOAT 
    ,CumulativeAdjustment FLOAT); 

INSERT INTO @Inflation 
SELECT T.YearNum, T.Inflation, T.YearNum, T.Inflation, T.Inflation 
FROM (VALUES 
    (2009, 0.05), 
    (2010, 0.05), 
    (2011, 0.05), 
    (2012, 0.05), 
    (2013, 0.05)) T (YearNum, Inflation) 

DECLARE @LoopBit BIT = 1; 

WHILE @LoopBit = 1 
BEGIN 
    UPDATE I SET 
     LowerCalc = IG.YearNum 
     ,AdjustedInflation = I.AdjustedInflation * (1.0 + IG.Inflation) 
     ,CumulativeAdjustment = I.CumulativeAdjustment + (I.AdjustedInflation * (1.0 + IG.Inflation)) 
    FROM @Inflation I 
    LEFT JOIN @Inflation IG ON IG.YearNum = I.LowerCalc - 1 
    WHERE I.LowerCalc > (SELECT MIN(YearNum) FROM @Inflation) 

    IF @@ROWCOUNT = 0 SET @LoopBit = 0 
END 

SELECT * 
FROM @Inflation 
+0

Ребята, большое спасибо за такой быстрый ответ !! Мне еще предстоит пройти через ответы и обновить его завтра. – Nagendra

+0

Дэвид, спасибо за быстрый ответ. Я не мог найти, где проблема ... но когда я меняю ставку на какое-то другое значение, результат не тот, который ожидался. Например, Ставки были 0,05, 0,06, 0,07, 0,08 и 0,05, я ожидал бы, что результирующие значения: 5,11,3,19,09,28,61828,35,049194 – Nagendra

2

Не имея PRODUCT() заявление, запрос становится немного более сложным, чем требовалось бы иначе, но это должно работать хорошо; он использует общие табличные выражения для каждого шага и логарифмические суммы для имитации PRODUCT().

WITH cte1 AS (
    SELECT a.year, 5 * EXP(SUM(COALESCE(LOG(b.rate),0))) rate 
    FROM inflation a LEFT JOIN inflation b ON a.year > b.year 
    GROUP BY a.year 
), cte2 AS (
    SELECT year, rate, SUM(rate) OVER (ORDER BY year) rate_sum FROM cte1 
) 
SELECT * FROM cte2 ORDER BY year 

An SQLfiddle to test with.

+0

+1 Довольно кратки. Обладает квадратичной сложностью, но таблица небольшая. –

+0

Ребята, большое спасибо за такой быстрый ответ !! Мне еще предстоит пройти через ответы и обновить его завтра. – Nagendra

+0

Хороший запрос. Короткие и сладкие. Я изменил его, как показано ниже, чтобы учитывать различные проценты, как показано ниже. Это заняло 7 мс. С помощью cte1 AS ( ВЫБЕРИТЕ a.year, ((a.rate-1) * 100) * EXP (СУММ (COALESCE (LOG (b.rate), 0))) ставка ОТ инфляции ЛЕВЫЙ ПРИСОЕДИНЕНИЕ инфляции b НА a .year> b.year GROUP BY a.year, a.rate ), cte2 А.С. ( выбора года, скорость, СУММА (скорость) НАД (ORDER BY года) rate_sum ОТ cte1 ) SELECT * FROM cte2 ORDER BY год – Nagendra

1

Если есть таблица инфляции (Yr INT, скорость с плавающей точкой), мы можем получить накопленную инфляцию следующим образом:

select i1.yr, i1.rate, EXP(SUM(LOG(1+i2.rate))) -1 AccRate 
from inflation I1 
inner join inflation I2 on i1.yr >=i2.yr 
group by i1.yr, i1.rate 
order by 1,2 

См http://sqlfiddle.com/#!6/508b5/1/0

+0

Wow. Это самое короткое. Не знал, что это так просто. Итак, что делает EXP (SUM (LOG (1 + i2.rate)))? Это сложная формула интереса? – Nagendra

+0

В общем случае EXP (SUM (LOG (MyColumn))) рассчитает произведение всех значений MyColumn. См. Это как замену функции агрегации PRODUCT(), которая не существует в SQL Server. –

0

Если уровень инфляции же каждый раз, как показано в вашем примере следующая формула поможет, нет необходимости в сложных запросах !:

declare @P float=5; 
declare @r float =0.05;--rate = 5/100 
declare @n float =1;-- 1 since compounded yearly 
declare @t float = 4;--For 4 years 

select @P* POWER (([email protected]/@n),(@n*@t)); 
--result = 6.07753125 
Смежные вопросы