2009-05-28 23 views
7

問題が発生しています。SQL Serverビュー:補間を使用して欠落している行を追加する方法

私は、毎日の財務省の値を保持するテーブルを定義していますyield curve

これは、値の履歴参照に使用される非常に単純なテーブルです。

はnotibly年468911-1921-29上の表内のいくつかのギャップがあります。

4年を計算するのはかなり簡単です。0.5*Year3Value + 0.5*Year5Valueです。

問題はどうすれば不足年数を返すことができるVIEWと書くことができますか?

おそらくストアドプロシージャで実行できますが、最終結果はビューである必要があります。

+0

年がない場合は、何を返すのですか?最も近い年の簡単な平均? – ahains

+0

大きな質問!誤解を招くタイトル:「MSSQLビュー:補間を使用して欠落行を追加する方法」または同様のものに変更してください。ありがとう。 – van

+0

タイトルが変更されました、良い提案 –

答えて

6

あなたが本当に望むのは線形補間であり、数年だけでなく数ヶ月も欠落しているという事実は、毎年ではなく月ごとに計算する必要があるということです。 I以下のコードについて

を使用すると、2つのテーブル(ビューの一部として計算することができるのいずれか)を有すると仮定する:

  • 収量:実データを含み、数オブにPeriodMが格納され月の名前ではなく。あなたがそこに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' 
+1

+1これは実際に私のために働くと思う、多くのありがとう! –

+0

私は結果セットを見ていて、曲線よりも正弦波のように見えました...元の式を見れば、0.5は1yrのギャップよりもうまく動作していないようです11-19私は15で0.5を計算し、それを上から下に計算すると、11が0.8 * [10yr] + 0.2 *(0.5 * [10yr] + 0.5 * [20yr]) - 15歳のように、12歳のときに.6と.4など。試してみる –

+0

@Christopher Klein:1)元の式は何ですか? 2)10 'と15'を使用して説明したように15 'を実行してから11'を実行すると、10と20を使用した場合と同じ番号になります。 – van

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 

このクエリは、見つかった最も近い年の平均をとっています。

+0

私は 'cal' CTEが再帰制限のために中断すると思います。 – van

+0

@van:確かに、リミッターを忘れてしまった。修正されました。 – Quassnoi

1

数字を&に設定すると、ピボットが解除されます。

その後行方不明の年に組合これはunpivotedlist どこYearNo中からYearNo 、(YearNo = YearNo-1 YearValueを選択)YearValue AS * 0.5 +(YearNo = YearNo + 1 YearValueを選択)* 0.5を選択し (私たちの年の欠けているリスト)

それからそれを再度ピボットして、必要なフォーマットを取得してビューにポップアップさせますか?

1

ギャップがある場合にカーブを2年間スムーズに移動させたいと思っていますので、1年以上が欠けている場合は、2つの最も近い年を平均化したくありません。ここで私はおそらく使用するものです:あなたは番号表(連番のちょうどテーブル)の代わりに、CTEを使用して、これを書き換えることができ

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 

。また、YC_BOT2のLEFT OUTER JOINとMINCとMAXを使ってNOT EXISTSやサブクエリを使用することもできます。この方法は混乱することがあります。

関連する問題