最近在给某家证券公司开发估值、减值系统,其中结构性存款模型有一处要对每一条数据按 起息日、到期日与债券付息频率 扩展成多条,扩展后每条的付息日期需在起息日的基础上按给定频率依次增加。
如果想在数据库中做这种处理,可以使用 connect by或循环 来实现。考虑到实际业务的需求实现较为复杂,这里做了简化,仅将重点放在数据库中一条数据如何扩展成多条数据上面。
扩展前后的数据样式
以如下两条数据为例,每条数据最终扩展会成几条,将由 结存起始日(D_PUTOUTDATE)、结存到期日(D_MATURITY)、债券付息频率(V_INTEREST_PAY_FREQ) 三者决定。
此处以债券付息频率M(月度Monthly)为例,扩展出的第一条数据的付息日期是在结存起始日基础上加30天(Monthly=30天),扩展出的第二条数据的付息日期在结存起始日基础上加60天… ,依次扩展下去,直到扩展出的最后一条的付息日期为不大于结存到期日的最大日期为止。如下即为以上两条样例数据扩展后的数据,可以看出交易编号为101的扩展出了2条,交易编号为102的扩展出了4条。
扩展的两种方式
为实现以上需求,可以使用connect by和循环两种方式。在此之前,先造出上面两条示例数据
--扩展前的测试数据
--drop table jc_str;
create table jc_str(
D_DATA_DT date --数据日期
,D_PUTOUTDATE date --结存起始日
,D_MATURITY date --结存到期日
,V_TRADE_NO VARCHAR2(20) --交易编号
,V_INTEREST_PAY_FREQ VARCHAR2(20) --债券付息频率
);
insert into jc_str(D_DATA_DT,D_PUTOUTDATE,D_MATURITY,V_TRADE_NO,V_INTEREST_PAY_FREQ)
values(to_date('20211201','yyyymmdd'),to_date('20220101','yyyymmdd'),to_date('20220310','yyyymmdd'),'101','M');commit;
insert into jc_str(D_DATA_DT,D_PUTOUTDATE,D_MATURITY,V_TRADE_NO,V_INTEREST_PAY_FREQ)
values(to_date('20211201','yyyymmdd'),to_date('20220101','yyyymmdd'),to_date('20220501','yyyymmdd'),'102','M');commit;
--存储扩展后数据的表
--drop table jc_pay;
create table jc_pay(
D_DATA_DT date --数据日期
,D_PUTOUTDATE date --结存起始日
,D_MATURITY date --结存到期日
,D_PAY_DATE date --付息日期
,V_TRADE_NO VARCHAR2(20) --交易编号
);
方式一:connect by
connect by方法适用于数据量不太大且没有复杂逻辑处理的数据扩展
declare
kz_max int; --所有需要扩展的数据中,扩展条数最多的条数
begin
--扩展的最大条数
select max(kz_num) into kz_max from (
select decode(V_INTEREST_PAY_FREQ
,'M',floor((D_MATURITY-D_PUTOUTDATE)/30)
) as kz_num
from jc_str
);
insert into jc_pay(
D_DATA_DT
,D_PUTOUTDATE
,D_MATURITY
,D_PAY_DATE
,V_TRADE_NO
)
with temp1 as (
select D_DATA_DT
,D_PUTOUTDATE
,D_MATURITY
,V_TRADE_NO
,V_INTEREST_PAY_FREQ
,decode(V_INTEREST_PAY_FREQ
,'M',floor((D_MATURITY-D_PUTOUTDATE)/30)
) as kz_num --计算每条数据需要扩展出的条数
from jc_str
)
select D_DATA_DT
,D_PUTOUTDATE
,D_MATURITY
,D_PUTOUTDATE+rn*decode(V_INTEREST_PAY_FREQ,'M',30) --计算付息日期
,V_TRADE_NO
from (--这里可以这样理解:每条数据都会先与括号里的connect by子句关联出kz_max条相同的数据(这kz_max条数据的rn从1到kz_max),然后筛选出rn<=kz_num(每条数据需要扩展出的条数)的数据,这样操作后每条数据只会留下自身要扩展的条数对应的数据。
select a.*,b.rn
from temp1 a
inner join (select rownum rn from dual connect by level<=kz_max) b
on b.rn<=a.kz_num
--order by a.V_TRADE_NO,b.rn
);
end;
方式二:循环
循环适用于有复杂逻辑处理的数据扩展,比如每一条的扩展要按不同频率做不同处理
declare
iter int:=1; --循环自增序列
pl_num int; --频率码值转换
begin
--循环每条数据(外循环)
for c in (
select D_DATA_DT,D_PUTOUTDATE,D_MATURITY,V_TRADE_NO,V_INTEREST_PAY_FREQ
from jc_str
)
loop
--频率判断转换
IF c.V_INTEREST_PAY_FREQ='M' THEN
pl_num:=30;
-- ELSIF c.PL='Q' THEN
-- pl_num:=120;
END IF;
--日期比较(内循环)
while c.D_PUTOUTDATE+iter*pl_num<=c.D_MATURITY loop
insert into jc_pay(D_DATA_DT
,D_PUTOUTDATE
,D_MATURITY
,D_PAY_DATE
,V_TRADE_NO
)
values(
c.D_DATA_DT
,c.D_PUTOUTDATE
,c.D_MATURITY
,c.D_PUTOUTDATE+iter*pl_num --付息日期计算
,c.V_TRADE_NO
);
iter:=iter+1;
end loop;
iter:=1;
end loop;
end;