2009-05-28 94 views
7

遇到问题。SQL Server视图:如何使用插值添加缺失的行

我有一张桌子定义为持有每日财政部的价值yield curve

这是一个非常简单的表格,用于历史查询值。

有notibly同比468911-1921-29表中的一些差距。

该公式非常简单,因为要计算年份4这是0.5*Year3Value + 0.5*Year5Value

问题是我该如何写一个VIEW可以返回缺失的年份?

我可以在存储过程中做到这一点,但最终的结果需要一个视图。

+0

当一年缺失时,您希望返回什么?近一年的简单平均值? – ahains 2009-05-28 13:32:28

+0

伟大的问题!标题误导虽然:请改变它像“MSSQL视图:如何使用插值添加缺失的行”或类似的东西。谢谢。 – van 2009-05-28 14:57:47

+0

标题改变,好建议 – 2009-05-28 18:21:02

回答

6

以假设汤姆H.,你真正想要的是一个线性插值和事实,不仅几年,而且还有几个月缺失,你需要基于每月的计算,而不是年。

对于代码下面我假定有2个表(其中一个可以被计算作为视图的一部分):

  • 收率:包含真实数据,并存储PeriodM在数OF-月而不是名字。如果存储PeriodName那里,你只需要加入在桌子上:
  • (可以像所示的视图来计算):商店时段名称和月数它代表

以下代码必须正常工作(您只需根据它创建一个视图):

WITH "Period" (PeriodM, PeriodName) AS (
    -- // I would store it as another table basically, but having it as part of the view would do 
       SELECT 01, '1 mo' 
    UNION ALL SELECT 02, '2 mo' -- // data not stored 
    UNION ALL SELECT 03, '3 mo' 
    UNION ALL SELECT 06, '6 mo' 
    UNION ALL SELECT 12, '1 yr' 
    UNION ALL SELECT 24, '2 yr' 
    UNION ALL SELECT 36, '3 yr' 
    UNION ALL SELECT 48, '4 yr' -- // data not stored 
    UNION ALL SELECT 60, '5 yr' 
    UNION ALL SELECT 72, '6 yr' -- // data not stored 
    UNION ALL SELECT 84, '7 yr' 
    UNION ALL SELECT 96, '8 yr' -- // data not stored 
    UNION ALL SELECT 108, '9 yr' -- // data not stored 
    UNION ALL SELECT 120, '10 yr' 
    -- ... // add more 
    UNION ALL SELECT 240, '20 yr' 
    -- ... // add more 
    UNION ALL SELECT 360, '30 yr' 
) 
, "Yield" (ID, PeriodM, Date, Value) AS (
    -- // ** This is the TABLE your data is stored in ** 
    -- // 
    -- // value of ID column is not important, but it must be unique (you may have your PK) 
    -- // ... it is used for a Tie-Breaker type of JOIN in the view 
    -- // 
    -- // This is just a test data: 
       SELECT 101, 01 /* '1 mo'*/, '2009-05-01', 0.06 
    UNION ALL SELECT 102, 03 /* '3 mo'*/, '2009-05-01', 0.16 
    UNION ALL SELECT 103, 06 /* '6 mo'*/, '2009-05-01', 0.31 
    UNION ALL SELECT 104, 12 /* '1 yr'*/, '2009-05-01', 0.49 
    UNION ALL SELECT 105, 24 /* '2 yr'*/, '2009-05-01', 0.92 
    UNION ALL SELECT 346, 36 /* '3 yr'*/, '2009-05-01', 1.39 
    UNION ALL SELECT 237, 60 /* '5 yr'*/, '2009-05-01', 2.03 
    UNION ALL SELECT 238, 84 /* '7 yr'*/, '2009-05-01', 2.72 
    UNION ALL SELECT 239,120 /*'10 yr'*/, '2009-05-01', 3.21 
    UNION ALL SELECT 240,240 /*'20 yr'*/, '2009-05-01', 4.14 
    UNION ALL SELECT 250,360 /*'30 yr'*/, '2009-05-01', 4.09 
) 
, "ReportingDate" ("Date") AS (
    -- // this should be a part of the view (or a separate table) 
    SELECT DISTINCT Date FROM "Yield" 
) 

-- // This is the Final VIEW that you want given the data structure as above 
SELECT  d.Date, p.PeriodName, --//p.PeriodM, 
      CAST(
       COALESCE(y_curr.Value, 
        ( (p.PeriodM - y_prev.PeriodM) * y_prev.Value 
        + (y_next.PeriodM - p.PeriodM) * y_next.Value 
        )/(y_next.PeriodM - y_prev.PeriodM) 
       ) AS DECIMAL(9,4) -- // TODO: cast to your type if not FLOAT 
      ) AS Value 
FROM  "Period" p 
CROSS JOIN "ReportingDate" d 
LEFT JOIN "Yield" y_curr 
     ON y_curr.Date = d.Date 
     AND y_curr.PeriodM = p.PeriodM 
LEFT JOIN "Yield" y_prev 
     ON y_prev.ID = (SELECT TOP 1 y.ID FROM Yield y WHERE y.Date = d.Date AND y.PeriodM <= p.PeriodM ORDER BY y.PeriodM DESC) 
LEFT JOIN "Yield" y_next 
     ON y_next.ID = (SELECT TOP 1 y.ID FROM Yield y WHERE y.Date = d.Date AND y.PeriodM >= p.PeriodM ORDER BY y.PeriodM ASC) 

--//WHERE  d.Date = '2009-05-01' 
0
WITh cal(year) AS 
     (
     SELECT 1 AS current_year 
     UNION ALL 
     SELECT year + 1 
     FROM cal 
     WHERE year < 100 
     ) 
SELECT CASE WHEN yield_year IS NULL THEN 
      0.5 * 
      (
      SELECT TOP 1 yield_value 
      FROM yield 
      WHERE yield_year < year 
      ORDER BY 
        yield_year DESC 
      ) + 
      0.5 * 
      (
      SELECT TOP 1 yield_value 
      FROM yield 
      WHERE yield_year > year 
      ORDER BY 
        yield_year ASC 
      ) 
     ELSE 
      yield_value 
     END 
FROM  cal 
LEFT JOIN 
     yield 
ON  yield_year = year 

对于未检索到的年份此搜索需大约多少年的时间才能发现。

1

您可以尝试unpivot以获取列表中的年份&值。

然后工会这对失踪多年 选择YearNo ,(选择YearValue其中YearNo = YearNo-1)×0.5 +(选择YearValue其中YearNo = YearNo + 1)* 0.5 AS YearValue 从unpivotedlist 其中YearNo在(我们缺少的年份列表)

然后将其重新转回备份以获取您需要的格式并将它弹出视图中?

1

我打算猜测,如果存在差距,您希望曲线在两年之间平滑移动,所以如果缺少一年以上,您不希望只是平均两个最近的年份。这里就是我可能会使用:

SELECT 
    NUM.number AS year, 
    COALESCE(YC.val, YC_BOT.val + ((NUM.number - YC_BOT.yr) * ((YC_TOP.val - YC_BOT.val)/(YC_TOP.yr - YC_BOT.yr)))) 
FROM 
    dbo.Numbers NUM 
LEFT OUTER JOIN dbo.Yield_Curve YC ON 
    YC.yr = NUM.number 
LEFT OUTER JOIN dbo.Yield_Curve YC_TOP ON 
    YC.yr IS NULL AND  -- Only join if we couldn't find a current year value 
    YC_TOP.yr > NUM.number 
LEFT OUTER JOIN dbo.Yield_Curve YC_TOP2 ON 
    YC_TOP2.yr > NUM.number AND 
    YC_TOP2.yr < YC_TOP.yr 
LEFT OUTER JOIN dbo.Yield_Curve YC_BOT ON 
    YC.yr IS NULL AND  -- Only join if we couldn't find a current year value 
    YC_BOT.yr < NUM.number 
LEFT OUTER JOIN dbo.Yield_Curve YC_BOT2 ON 
    YC_BOT2.yr < NUM.number AND 
    YC_BOT2.yr > YC_BOT.yr 
WHERE 
    YC_TOP2.yr IS NULL AND 
    YC_BOT2.yr IS NULL AND 
    NUM.number BETWEEN @low_yr AND @high_yr 

你可以使用CTE来代替数字表(只是一个连续的数字表)的改写本。如果你想这样做,你也可以在MIN和MAX中使用NOT EXISTS或子查询,而不是在YC_BOT2和YC_TOP2上使用LEFT OUTER JOIN。有些人觉得这种方法很混乱。