SQL 服务器查询以在不存在当前日期值时从前一天结转值
SQL Server Query to carry over values from previous day when no current day values exists
我有一个跟踪交易者每个交易时段的风险资本的查询。当某一天没有交易时,我需要将最后一个活跃日的日终值结转到下一个活跃日。这是我目前所拥有的:
DECLARE @Start DATETIME = '2019-08-20'
DECLARE @End DATETIME = '2019-08-27'
DECLARE @history TABLE( Id INT, AccountId INT, AllocatedCapital MONEY, RunningAllocatedCapital MONEY,RN INT,SessionDate DATETIME)
INSERT INTO @history(Id, AccountId, AllocatedCapital, RunningAllocatedCapital,RN,SessionDate)
VALUES (362082, 1182, -170150.0000, -170150.0000, 1, '2019-08-20'),
(362090, 1182, -4167.9600, -199466.4600, 1, '2019-08-21'),
(362088, 1182, -10330.0000, -195298.5000, 2, '2019-08-21'),
(362086, 1182, -9454.5000, -184968.5000, 3, '2019-08-21'),
(362084, 1182, -5364.0000, -175514.0000, 4, '2019-08-21'),
(362094, 1182, -4140.0000, -203606.4600, 1, '2019-08-22'),
(362092, 1182, -4140.0000, -207746.4600, 2, '2019-08-22'),
(362105, 1182, 4140.0000, -187052.4800, 1, '2019-08-27')
;WITH tradingdays as (
SELECT TOP (DATEDIFF(DAY, @Start, @End) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1,@Start) SessionDate
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)
SELECT -MIN(RunningAllocatedCapital) MaxCapitalAtRisk,
-MAX(CASE H.RN WHEN 1 THEN H.RunningAllocatedCapital END)EodCapitalAtRisk,
C.SessionDate
FROM tradingdays C
LEFT JOIN @history H ON H.SessionDate = C.SessionDate
WHERE DATENAME(dw,C.SessionDate) NOT IN ('Saturday','Sunday')
GROUP BY C.SessionDate, H.SessionDate
ORDER BY C.SessionDate
而不是这个:
MaxCapitalAtRisk EodCapitalAtRisk SessionDate
170150.00 170150.00 2019-08-20 00:00:00.000
199466.46 199466.46 2019-08-21 00:00:00.000
207746.46 203606.4600 2019-08-22 00:00:00.000
NULL NULL 2019-08-23 00:00:00.000
NULL NULL 2019-08-26 00:00:00.000
187052.48 187052.48 2019-08-27 00:00:00.000
我的结果集应该如下所示:
MaxCapitalAtRisk EodCapitalAtRisk SessionDate
170150.00 170150.00 2019-08-20 00:00:00.000
199466.46 199466.46 2019-08-21 00:00:00.000
207746.46 203606.46 2019-08-22 00:00:00.000
203606.46 203606.46 2019-08-23 00:00:00.000
203606.46 203606.46 2019-08-26 00:00:00.000
187052.48 187052.48 2019-08-27 00:00:00.000
我知道在 SQL 服务器中有一些干净的方法可以在不使用子查询或游标的情况下执行此操作,但我不记得如何执行此操作。
基本上,您正在寻找带有 ignore nulls
选项的 lag()
。 SQL服务器不支持,但我们可以用间隙和孤岛技术模拟它。
我们的想法是构建由一个 "regular" 记录(岛)和 0 到 N "missing" 记录(间隙)组成的记录组,使用条件求和或计数。然后,我们可以使用 first_value()
来填补空岛的价值:
with tradingdays as (
select @start SessionDate
union all select dateadd(day, 1, SessionDate) from tradingdays where SessionDate < @end
)
select
SessionDate,
first_value(MaxCapitalAtRisk) over(partition by grp order by SessionDate) MaxCapitalAtRisk,
first_value(EodCapitalAtRisk) over(partition by grp order by SessionDate) EodCapitalAtRisk
from (
select
td.SessionDate,
- min(RunningAllocatedCapital) MaxCapitalAtRisk,
- max(case h.rn when 1 then h.runningallocatedcapital end) EodCapitalAtRisk,
count(h.SessionDate) over(order by td.SessionDate) grp
from tradingdays td
left join @history h on h.SessionDate = td.SessionDate
where datename(dw, td.SessionDate) not in ('Saturday', 'Sunday')
group by td.SessionDate, h.SessionDate
) t
order by SessionDate
我更改了生成日期的通用 table 表达式以使用递归,因为我发现它更容易理解 - 但这不会改变逻辑,如果您愿意,可以切换回原来的 cte它更好。如果你坚持我的 cte,并且你的日期分布超过 100 天,那么你需要在查询的最后添加 option(maxrecursion 0)
。
SessionDate | MaxCapitalAtRisk | EodCapitalAtRisk
:---------------------- | :--------------- | :---------------
2019-08-20 00:00:00.000 | 170150.0000 | 170150.0000
2019-08-21 00:00:00.000 | 199466.4600 | 199466.4600
2019-08-22 00:00:00.000 | 207746.4600 | 207746.4600
2019-08-23 00:00:00.000 | 207746.4600 | 207746.4600
2019-08-26 00:00:00.000 | 207746.4600 | 207746.4600
2019-08-27 00:00:00.000 | 187052.4800 | 187052.4800
我有一个跟踪交易者每个交易时段的风险资本的查询。当某一天没有交易时,我需要将最后一个活跃日的日终值结转到下一个活跃日。这是我目前所拥有的:
DECLARE @Start DATETIME = '2019-08-20'
DECLARE @End DATETIME = '2019-08-27'
DECLARE @history TABLE( Id INT, AccountId INT, AllocatedCapital MONEY, RunningAllocatedCapital MONEY,RN INT,SessionDate DATETIME)
INSERT INTO @history(Id, AccountId, AllocatedCapital, RunningAllocatedCapital,RN,SessionDate)
VALUES (362082, 1182, -170150.0000, -170150.0000, 1, '2019-08-20'),
(362090, 1182, -4167.9600, -199466.4600, 1, '2019-08-21'),
(362088, 1182, -10330.0000, -195298.5000, 2, '2019-08-21'),
(362086, 1182, -9454.5000, -184968.5000, 3, '2019-08-21'),
(362084, 1182, -5364.0000, -175514.0000, 4, '2019-08-21'),
(362094, 1182, -4140.0000, -203606.4600, 1, '2019-08-22'),
(362092, 1182, -4140.0000, -207746.4600, 2, '2019-08-22'),
(362105, 1182, 4140.0000, -187052.4800, 1, '2019-08-27')
;WITH tradingdays as (
SELECT TOP (DATEDIFF(DAY, @Start, @End) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1,@Start) SessionDate
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)
SELECT -MIN(RunningAllocatedCapital) MaxCapitalAtRisk,
-MAX(CASE H.RN WHEN 1 THEN H.RunningAllocatedCapital END)EodCapitalAtRisk,
C.SessionDate
FROM tradingdays C
LEFT JOIN @history H ON H.SessionDate = C.SessionDate
WHERE DATENAME(dw,C.SessionDate) NOT IN ('Saturday','Sunday')
GROUP BY C.SessionDate, H.SessionDate
ORDER BY C.SessionDate
而不是这个:
MaxCapitalAtRisk EodCapitalAtRisk SessionDate
170150.00 170150.00 2019-08-20 00:00:00.000
199466.46 199466.46 2019-08-21 00:00:00.000
207746.46 203606.4600 2019-08-22 00:00:00.000
NULL NULL 2019-08-23 00:00:00.000
NULL NULL 2019-08-26 00:00:00.000
187052.48 187052.48 2019-08-27 00:00:00.000
我的结果集应该如下所示:
MaxCapitalAtRisk EodCapitalAtRisk SessionDate
170150.00 170150.00 2019-08-20 00:00:00.000
199466.46 199466.46 2019-08-21 00:00:00.000
207746.46 203606.46 2019-08-22 00:00:00.000
203606.46 203606.46 2019-08-23 00:00:00.000
203606.46 203606.46 2019-08-26 00:00:00.000
187052.48 187052.48 2019-08-27 00:00:00.000
我知道在 SQL 服务器中有一些干净的方法可以在不使用子查询或游标的情况下执行此操作,但我不记得如何执行此操作。
基本上,您正在寻找带有 ignore nulls
选项的 lag()
。 SQL服务器不支持,但我们可以用间隙和孤岛技术模拟它。
我们的想法是构建由一个 "regular" 记录(岛)和 0 到 N "missing" 记录(间隙)组成的记录组,使用条件求和或计数。然后,我们可以使用 first_value()
来填补空岛的价值:
with tradingdays as (
select @start SessionDate
union all select dateadd(day, 1, SessionDate) from tradingdays where SessionDate < @end
)
select
SessionDate,
first_value(MaxCapitalAtRisk) over(partition by grp order by SessionDate) MaxCapitalAtRisk,
first_value(EodCapitalAtRisk) over(partition by grp order by SessionDate) EodCapitalAtRisk
from (
select
td.SessionDate,
- min(RunningAllocatedCapital) MaxCapitalAtRisk,
- max(case h.rn when 1 then h.runningallocatedcapital end) EodCapitalAtRisk,
count(h.SessionDate) over(order by td.SessionDate) grp
from tradingdays td
left join @history h on h.SessionDate = td.SessionDate
where datename(dw, td.SessionDate) not in ('Saturday', 'Sunday')
group by td.SessionDate, h.SessionDate
) t
order by SessionDate
我更改了生成日期的通用 table 表达式以使用递归,因为我发现它更容易理解 - 但这不会改变逻辑,如果您愿意,可以切换回原来的 cte它更好。如果你坚持我的 cte,并且你的日期分布超过 100 天,那么你需要在查询的最后添加 option(maxrecursion 0)
。
SessionDate | MaxCapitalAtRisk | EodCapitalAtRisk :---------------------- | :--------------- | :--------------- 2019-08-20 00:00:00.000 | 170150.0000 | 170150.0000 2019-08-21 00:00:00.000 | 199466.4600 | 199466.4600 2019-08-22 00:00:00.000 | 207746.4600 | 207746.4600 2019-08-23 00:00:00.000 | 207746.4600 | 207746.4600 2019-08-26 00:00:00.000 | 207746.4600 | 207746.4600 2019-08-27 00:00:00.000 | 187052.4800 | 187052.4800