2013-04-14 16 views
1

我想知道是否有一些新的SQL Server 2012功能可以解決這個問題。這是我的DDL和樣本數據如何使用SQL Server 2012計算峯谷縮減?

CREATE TABLE [dbo].[transactions] 
(
    [transactionId] [int]   NOT NULL, 
    [dt]   [datetime]  NOT NULL, 
    [balance]  [decimal](22, 6) NULL 
);  
GO 

INSERT [dbo].[transactions] ([transactionId], [dt], [balance]) VALUES 
(174, CAST(0x0000A19600000000 AS DateTime), CAST(1000.000000 AS Decimal(22, 6))), 
(178, CAST(0x0000A19700869520 AS DateTime), CAST(1100.000000 AS Decimal(22, 6))), 
(179, CAST(0x0000A19700933780 AS DateTime), CAST(1212.000000 AS Decimal(22, 6))), 
(180, CAST(0x0000A19700B4B9A0 AS DateTime), CAST(1342.000000 AS Decimal(22, 6))), 
(181, CAST(0x0000A19700BB0AD0 AS DateTime), CAST(1198.000000 AS Decimal(22, 6))), 
(182, CAST(0x0000A19700E67030 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))), 
(183, CAST(0x0000A19700F358E0 AS DateTime), CAST(900.000000 AS Decimal(22, 6))), 
(184, CAST(0x0000A19700F58B60 AS DateTime), CAST(876.000000 AS Decimal(22, 6))), 
(185, CAST(0x0000A19700F9AA10 AS DateTime), CAST(889.000000 AS Decimal(22, 6))), 
(186, CAST(0x0000A19701034700 AS DateTime), CAST(1133.000000 AS Decimal(22, 6))), 
(187, CAST(0x0000A19A0089E0E0 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))), 
(191, CAST(0x0000A19A009450C0 AS DateTime), CAST(1566.000000 AS Decimal(22, 6))), 
(192, CAST(0x0000A19A00A5E4C0 AS DateTime), CAST(1800.000000 AS Decimal(22, 6))), 
(188, CAST(0x0000A19A00AA49C0 AS DateTime), CAST(1900.000000 AS Decimal(22, 6))), 
(189, CAST(0x0000A19A00B54640 AS DateTime), CAST(1456.000000 AS Decimal(22, 6))), 
(190, CAST(0x0000A19A00CAB2A0 AS DateTime), CAST(1234.000000 AS Decimal(22, 6))), 
(193, CAST(0x0000A19A00F12660 AS DateTime), CAST(1400.000000 AS Decimal(22, 6))), 
(195, CAST(0x0000A19A010087E0 AS DateTime), CAST(1444.000000 AS Decimal(22, 6))), 
(196, CAST(0x0000A19E00C7F380 AS DateTime), CAST(1556.000000 AS Decimal(22, 6))), 
(197, CAST(0x0000A19E00FE5560 AS DateTime), CAST(1975.000000 AS Decimal(22, 6))); 

我在系列的平衡的最大峯谷縮減之後,按dt排序。峯谷谷值下降是在前一個高點交叉之前,在平衡高點到最低點的最大百分比變化。在這裏更好地描述http://www.investopedia.com/terms/p/peak-to-valley-drawdown.asp在這個數據集中,我們有兩個縮進。

第一個是從1342.00至876.00(-34.72%)和從1900年第二個到1234(-35.05%)

到谷百分比縮編最大峯在這組因此,是-35.05%。我需要一個可以提供此值的SQL Server查詢。如果可能的話,寧願不必使用臨時表。有任何想法嗎?

+0

@AaronBertrand感謝編輯 –

回答

1

如果第一項是峯值

;with trnsCTE (ID,bal) AS 
    ( -- get seqential ID 
     SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance] 
     from [transactions] 
), 
    trnsCTE2 (ID,bal) AS 
    (-- any peaks 
     select t2.ID, t2.bal 
     from trnsCTE as T1 
     join trnsCTE as T2 
     on (t2.ID = t1.ID+1 
     and t2.bal > t1.bal) 
     join trnsCTE as T3 
     on t3.ID = t2.ID+1 
     and t3.bal < t1.bal 
    ) 
    , 
    trnsCTE3 (ID,bal) AS 
    ( -- get first peak and then bigger peaks only 
     SELECT distinct T1.ID, T1.BAL 
     from trnsCTE2 as T1 
     where T1.ID = (select min(ID) from trnsCTE2) 
     or T1.bal > (select max(bal) from trnsCTE2 where trnsCTE2.ID < t1.ID) 
    ) 
    -- calculate 
    select t1.id, t1.bal, min(trnsCTE.bal), (t1.bal - min(trnsCTE.bal)) * 100/t1.bal 
    from trnsCTE 
    join trnsCTE3 t1 
    on t1.id < trnsCTE.id 
    and (trnsCTE.id < (select min(id) from trnsCTE3 where id > t1.id) 
      or 
      t1.id = (select max(id) from trnsCTE3)) 
    group by t1.id, t1.bal 
    order by t1.id 

這直接轉化爲一個#temps 沒有用#TEMP在任擇議定書表示不希望用戶#TEMP這將錯過

insert into #trnsCTE (ID,bal) 
SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance] 
from [transactions] 
+0

,這給了我想要什麼,但表現相當糟糕。也許我需要一個臨時表解決方案? –

+0

你的問題指出「不用臨時表」。正確的結果與該約束,甚至沒有+1的禮貌。 – Paparazzi

+0

沒有違法意圖。在只有幾百行的數據集上,查詢花費了大約30秒的時間。我只是想知道在一個T-SQL語句中是否有一種優雅有效的方法來實現它。 –

2

我不知道任何SQL Server 2012的功能將任何比這更簡潔地或高效地獲取此值:

;WITH x AS 
(
    SELECT [drop] = ((s.balance-e.balance)*100.0/s.balance) 
    FROM dbo.transactions AS s 
    INNER JOIN dbo.transactions AS e 
    ON s.transactionId < e.transactionId 
    AND s.balance > e.balance 
) 
SELECT [Largest Drawdown] = -MAX([drop]) FROM x; 

結果:

Largest Drawdown 
---------------- 
-35.05263157894 

我坦白,雖然,這僅適用於您的示例數據,因爲您的山谷便於解決您想要解決的問題。如果將第4行更改爲875,則此查詢將認爲該集合的一部分。換句話說,我已經在整個範圍內計算了下拉,而不是直到再次穿過高點時的範圍。

我懷疑有更好的方法來解決這個查詢使用間隙/孤島技術,我會盡力回到它,當我可以充分地集中它。

+0

是的,我需要集合中的一系列「峯值」後最大的百分比縮減。 –

0
select peak_dt, peak_balance, trough_dt, trough_balance, (peak_balance - trough_balance) * 100.0/peak_balance as drawdown 
from (
    select dt as peak_dt, balance as peak_balance, nullif(last_value(dt) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), dt) as trough_dt, nullif(last_value(balance) over (partition by peak_valley_group order by dt rows between unbounded preceding and unbounded following), balance) as trough_balance, isPeak 
    from (
     select *, sum(isPeak) over (order by dt) as peak_valley_group 
     from (
      select dt, balance, (case when forward_trend = -1 then 1 else 0 end) as isPeak, max(balance) over (partition by forward_trend order by dt) as current_max_balance 
      from (
       -- Nulls for lead/lag here produce the desired result 
       select *, (case when lead(balance, 1) over (order by dt) > balance then 1 else -1 end) as forward_trend, (case when lag(balance, 1) over (order by dt) > balance then 1 else -1 end) as backward_trend 
       from transactions 
      ) t 
      where forward_trend = backward_trend 
     ) t 
     where (isPeak = 1 and balance = current_max_balance) 
     or isPeak = 0 
    ) t 
) t 
where isPeak = 1 
order by peak_dt