我想知道一些新的SQL Server 2012功能是否可以帮助解决这个问题。这是我的DDL和示例数据
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 :(得分: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,则此查询将认为该集合的一部分。换句话说,我在这里计算了整个范围的下降,而不是直到再次超过高点的范围。
我怀疑有一种更好的方法可以使用gap / island技术来解决这个问题,当我可以充分专注于它时,我会尝试回到它。
答案 1 :(得分: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 没有在OP上使用#temp表示不想用户#temp
insert into #trnsCTE (ID,bal)
SELECT ROW_NUMBER() OVER (ORDER BY DT) as ID, [balance]
from [transactions]
答案 2 :(得分: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