SQL 中的双线性插值

发布于 2024-11-07 03:43:40 字数 2238 浏览 0 评论 0原文

对于我正在做的项目,我需要基于 2 个维度(“自 1900 年以来的天数”和收益率)插值一个值(利率),

我目前有以下代码,其中 f 是插值:

declare @rates table (
    days int,
    yield int,
    rate decimal(18,6)
)

insert into @rates (days,yield,rate)
values (1,30,0.1),
(1,90,0.2),
(3,30,0.2),
(null,3,90,0.4)

declare @data table(
    id int,
    days int,
    yield int)

insert into @data(id,date,days,yield) values
(1,2,60)


select r.*
-- calculation below does not work if x or y ends up being the same 
    -- (because they all cancel each other out)
,finalinterp = ((f11/((x2 - x1)*(y12 - y11)))*(x2 - x)*(y12 - y))
+ ((f21/((x2 - x1)*(y22 - y21)))*(x - x1)*(y22 - y))
+ ((f12/((x2 - x1)*(y12 - y11)))*(x2 - x)*(y - y11))
+ ((f22/((x2 - x1)*(y22 - y21)))*(x - x1)*(y - y21))

from
(
select id,d.days as x,isnull(x1,x2) x1 ,isnull(x2,x1) x2,d.yield as y,
ISNULL(y11,y12) y11,ISNULL(y12,y11) y12,ISNULL(y21,y22) y21,ISNULL(y22,y21) y22,
r11.rate as f11,
r12.rate as f12,
r21.rate as f21,
r22.rate as f22

from @data d
cross apply
(   
    select MAX(r.days)  as x1 from @rates r1
    where r.days <= d.days
) xt1
cross apply
(
    select MIN(r.days)  as x2 from @rates r1
    where days >= d.days
) xt2
cross apply
(
    select MAX(yield) as y11 from @rates r1
    where r1.days = isnull(x1,x2)
    and yield <= d.yield
) yt1
cross apply
(
    select MIN(yield) as y12 from @rates r1
    where r1.days = isnull(x1,x2)
    and yield >= d.yield
) yt2
cross apply
(
    select MAX(yield) as y21 from @rates r1
    where r1.days = isnull(x2,x1)
    and yield <= d.yield
) yt3
cross apply
(
    select MIN(yield) as y22 from @rates r1
    where r1.days = isnull(x2,x1)
    and yield >= d.yield
) yt4
left outer join @rates r11 on r11.mdays = isnull(x1,x2) and r11.yield = ISNULL(y11,y12)
left outer join @rates r12 on r12.mdays = isnull(x1,x2) and r12.yield = ISNULL(y12,y11)
left outer join @rates r21 on r21.mdays = isnull(x2,x1) and r21.yield = ISNULL(y21,y22)
left outer join @rates r22 on r22.mdays = isnull(x2,x1) and r22.yield = ISNULL(y22,y21)
) r

目前,这适用于正确解释的值但是,如果该值实际存在(例如,如果我设置 data.yield = 90 或 data.days = 1),则不需要进行插值,它会在尝试除以零时崩溃。

有人能弄清楚如何让它在这种情况下工作吗?

还有更有效的方法吗?在现实世界中,同一查询中存在其他表的整体混搭,因此越简洁越好,

谢谢

For a project I am doing I need to interpolate a value (interest rate)based on 2 dimensions ('days since 1900' and yield)

I currently have the following code where f is the interpolated value:

declare @rates table (
    days int,
    yield int,
    rate decimal(18,6)
)

insert into @rates (days,yield,rate)
values (1,30,0.1),
(1,90,0.2),
(3,30,0.2),
(null,3,90,0.4)

declare @data table(
    id int,
    days int,
    yield int)

insert into @data(id,date,days,yield) values
(1,2,60)


select r.*
-- calculation below does not work if x or y ends up being the same 
    -- (because they all cancel each other out)
,finalinterp = ((f11/((x2 - x1)*(y12 - y11)))*(x2 - x)*(y12 - y))
+ ((f21/((x2 - x1)*(y22 - y21)))*(x - x1)*(y22 - y))
+ ((f12/((x2 - x1)*(y12 - y11)))*(x2 - x)*(y - y11))
+ ((f22/((x2 - x1)*(y22 - y21)))*(x - x1)*(y - y21))

from
(
select id,d.days as x,isnull(x1,x2) x1 ,isnull(x2,x1) x2,d.yield as y,
ISNULL(y11,y12) y11,ISNULL(y12,y11) y12,ISNULL(y21,y22) y21,ISNULL(y22,y21) y22,
r11.rate as f11,
r12.rate as f12,
r21.rate as f21,
r22.rate as f22

from @data d
cross apply
(   
    select MAX(r.days)  as x1 from @rates r1
    where r.days <= d.days
) xt1
cross apply
(
    select MIN(r.days)  as x2 from @rates r1
    where days >= d.days
) xt2
cross apply
(
    select MAX(yield) as y11 from @rates r1
    where r1.days = isnull(x1,x2)
    and yield <= d.yield
) yt1
cross apply
(
    select MIN(yield) as y12 from @rates r1
    where r1.days = isnull(x1,x2)
    and yield >= d.yield
) yt2
cross apply
(
    select MAX(yield) as y21 from @rates r1
    where r1.days = isnull(x2,x1)
    and yield <= d.yield
) yt3
cross apply
(
    select MIN(yield) as y22 from @rates r1
    where r1.days = isnull(x2,x1)
    and yield >= d.yield
) yt4
left outer join @rates r11 on r11.mdays = isnull(x1,x2) and r11.yield = ISNULL(y11,y12)
left outer join @rates r12 on r12.mdays = isnull(x1,x2) and r12.yield = ISNULL(y12,y11)
left outer join @rates r21 on r21.mdays = isnull(x2,x1) and r21.yield = ISNULL(y21,y22)
left outer join @rates r22 on r22.mdays = isnull(x2,x1) and r22.yield = ISNULL(y22,y21)
) r

Currently this works for a properly interpreted value however if the value actually exists (e.g. if I set data.yield = 90 or data.days = 1), thus doesn't need to be interpolated, it falls apart as it tries to do a divide by zero.

Can someone figure out how to make it work in this condition?

Also is there a more efficient way of doing this? In the real world there is a whole mashup of other tables in the same query so the more concise the better

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

琉璃梦幻 2024-11-14 03:43:40

有兴趣的朋友可以在下面回答。没有测试过性能。

如果 x 小于 x1,则使用 x1 值,依此类推,当 x > 时x2 和 y。

declare @rates table (
    mdate datetime,
    mdays int,
    yield int,
    rate decimal(18,6)
)

insert into @rates (mdate,mdays,yield,rate)
values (null,1,30,0.23),
    (null,1,90,0.36),
    (null,31,30,0.25),
    (null,31,90,0.37)

declare @data table(
    did int,
    ddate datetime,
    ddays int,
    yield int)


insert into @data(did,ddate,ddays,yield) values
(1,null,32,30)


select r2.*,
f = ((f11/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y12 - y11,0),1))) * isnull(convert(float,nullif(x2 - x,0)),0.5) * isnull(convert(float,nullif(y12 - y,0)),0.5))
 + ((f21/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y22 - y21,0),1))) * isnull(convert(float,nullif(x - x1,0)),0.5) * isnull(convert(float,nullif(y22 - y,0)),0.5))
 + ((f12/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y12 - y11,0),1))) * isnull(convert(float,nullif(x2 - x,0)),0.5) * isnull(convert(float,nullif(y - y11,0)),0.5))
 + ((f22/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y22 - y21,0),1))) * isnull(convert(float,nullif(x - x1,0)),0.5) * isnull(convert(float,nullif(y - y21,0)),0.5))
from
    (
        select 
        case when x > x2 then x2
            when x < x1 then x1
            else x end as x,
        case when y > y22 then y22
            when y < y11 then y11
            else y end as y,
        x1,x2,y11,y12,y21,y22,f11,f12,f21,f22
    from
    (
        select did,ddays as x,isnull(x1,x2) x1 ,isnull(x2,x1) x2,d.yield as y,ISNULL(y11,y12) y11,ISNULL(y12,y11) y12,ISNULL(y21,y22) y21,ISNULL(y22,y21) y22,
        r11.rate as f11,
        r12.rate as f12,
        r21.rate as f21,
        r22.rate as f22

        from @data d
        cross apply
        (   
        select MAX(mdays)  as x1 from @rates r1
        where mdays <= d.ddays
        ) xt1
    cross apply
    (
        select MIN(mdays)  as x2 from @rates r1
        where mdays >= d.ddays
    ) xt2
    cross apply
    (
        select MAX(yield) as y11 from @rates r1
        where r1.mdays = isnull(x1,x2)
        and yield <= d.yield
    ) yt1
    cross apply
    (
        select MIN(yield) as y12 from @rates r1
        where r1.mdays = isnull(x1,x2)
        and yield >= d.yield
    ) yt2
    cross apply
    (
        select MAX(yield) as y21 from @rates r1
        where r1.mdays = isnull(x2,x1)
        and yield <= d.yield
    ) yt3
    cross apply
    (
        select MIN(yield) as y22 from @rates r1
        where r1.mdays = isnull(x2,x1)
        and yield >= d.yield
    ) yt4
    left outer join @rates r11 on r11.mdays = isnull(x1,x2) and r11.yield = ISNULL(y11,y12)
    left outer join @rates r12 on r12.mdays = isnull(x1,x2) and r12.yield = ISNULL(y12,y11)
    left outer join @rates r21 on r21.mdays = isnull(x2,x1) and r21.yield = ISNULL(y21,y22)
    left outer join @rates r22 on r22.mdays = isnull(x2,x1) and r22.yield = ISNULL(y22,y21)
) r
)r2

Answer below for anyone interested. Haven't performance tested.

If x is less than x1, x1 value is used and so on for x > x2 and y.

declare @rates table (
    mdate datetime,
    mdays int,
    yield int,
    rate decimal(18,6)
)

insert into @rates (mdate,mdays,yield,rate)
values (null,1,30,0.23),
    (null,1,90,0.36),
    (null,31,30,0.25),
    (null,31,90,0.37)

declare @data table(
    did int,
    ddate datetime,
    ddays int,
    yield int)


insert into @data(did,ddate,ddays,yield) values
(1,null,32,30)


select r2.*,
f = ((f11/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y12 - y11,0),1))) * isnull(convert(float,nullif(x2 - x,0)),0.5) * isnull(convert(float,nullif(y12 - y,0)),0.5))
 + ((f21/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y22 - y21,0),1))) * isnull(convert(float,nullif(x - x1,0)),0.5) * isnull(convert(float,nullif(y22 - y,0)),0.5))
 + ((f12/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y12 - y11,0),1))) * isnull(convert(float,nullif(x2 - x,0)),0.5) * isnull(convert(float,nullif(y - y11,0)),0.5))
 + ((f22/(isnull(nullif(x2 - x1,0),1) * isnull(nullif(y22 - y21,0),1))) * isnull(convert(float,nullif(x - x1,0)),0.5) * isnull(convert(float,nullif(y - y21,0)),0.5))
from
    (
        select 
        case when x > x2 then x2
            when x < x1 then x1
            else x end as x,
        case when y > y22 then y22
            when y < y11 then y11
            else y end as y,
        x1,x2,y11,y12,y21,y22,f11,f12,f21,f22
    from
    (
        select did,ddays as x,isnull(x1,x2) x1 ,isnull(x2,x1) x2,d.yield as y,ISNULL(y11,y12) y11,ISNULL(y12,y11) y12,ISNULL(y21,y22) y21,ISNULL(y22,y21) y22,
        r11.rate as f11,
        r12.rate as f12,
        r21.rate as f21,
        r22.rate as f22

        from @data d
        cross apply
        (   
        select MAX(mdays)  as x1 from @rates r1
        where mdays <= d.ddays
        ) xt1
    cross apply
    (
        select MIN(mdays)  as x2 from @rates r1
        where mdays >= d.ddays
    ) xt2
    cross apply
    (
        select MAX(yield) as y11 from @rates r1
        where r1.mdays = isnull(x1,x2)
        and yield <= d.yield
    ) yt1
    cross apply
    (
        select MIN(yield) as y12 from @rates r1
        where r1.mdays = isnull(x1,x2)
        and yield >= d.yield
    ) yt2
    cross apply
    (
        select MAX(yield) as y21 from @rates r1
        where r1.mdays = isnull(x2,x1)
        and yield <= d.yield
    ) yt3
    cross apply
    (
        select MIN(yield) as y22 from @rates r1
        where r1.mdays = isnull(x2,x1)
        and yield >= d.yield
    ) yt4
    left outer join @rates r11 on r11.mdays = isnull(x1,x2) and r11.yield = ISNULL(y11,y12)
    left outer join @rates r12 on r12.mdays = isnull(x1,x2) and r12.yield = ISNULL(y12,y11)
    left outer join @rates r21 on r21.mdays = isnull(x2,x1) and r21.yield = ISNULL(y21,y22)
    left outer join @rates r22 on r22.mdays = isnull(x2,x1) and r22.yield = ISNULL(y22,y21)
) r
)r2
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文