【问题标题】:Best way to interpolate values in SQL在 SQL 中插入值的最佳方法
【发布时间】:2011-07-04 09:09:44
【问题描述】:

我有一张特定日期的汇率表:

          Rates

Id  |     Date      |  Rate
----+---------------+-------
 1  |   01/01/2011  |  4.5
 2  |   01/04/2011  |  3.2
 3  |   04/06/2011  |  2.4
 4  |   30/06/2011  |  5

我想根据简单的线性插值获得输出速率。

所以如果我输入 17/06/2011:

Date        Rate
----------  -----
01/01/2011  4.5
01/04/2011  3.2
04/06/2011  2.4
17/06/2011  
30/06/2011  5.0

线性插值是(5 + 2,4) / 2 = 3,7

有没有办法做一个简单的查询(SQL Server 2005),或者这种事情需要以编程方式完成(C#...)?

【问题讨论】:

    标签: sql sql-server-2005 interpolation linear-interpolation


    【解决方案1】:

    类似这样的东西(已更正):

    SELECT CASE WHEN next.Date IS NULL  THEN prev.Rate
                WHEN prev.Date IS NULL  THEN next.Rate
                WHEN next.Date = prev.Date  THEN prev.Rate
                  ELSE ( DATEDIFF(d, prev.Date, @InputDate) * next.Rate 
                       + DATEDIFF(d, @InputDate, next.Date) * prev.Rate
                       ) / DATEDIFF(d, prev.Date, next.Date)
           END AS interpolationRate 
    FROM
      ( SELECT TOP 1 
            Date, Rate 
        FROM Rates
        WHERE Date <= @InputDate
        ORDER BY Date DESC
      ) AS prev
      CROSS JOIN
      ( SELECT TOP 1 
            Date, Rate 
        FROM Rates
        WHERE Date >= @InputDate
        ORDER BY Date ASC
      ) AS next
    

    【讨论】:

    • 这当然是他想要的。我刚刚实施了一个糟糕的特例。顺便提一句。在最后一个 DATEDIFF 之前有一个括号,前两个 CASE WHEN 应该返回 Rate 而不是 Date。无论如何 +1。
    【解决方案2】:

    正如@Mark 已经指出的那样,CROSS JOIN 有其局限性。只要目标值超出定义值的范围,就不会返回任何记录。

    此外,上述解决方案仅限于一种结果。对于我的项目,我需要对整个 x 值列表进行插值,并提出了以下解决方案。也许其他读者也感兴趣?

    -- generate some grid data values in table #ddd:
    CREATE TABLE #ddd (id int,x float,y float, PRIMARY KEY(id,x));
    INSERT INTO  #ddd VALUES (1,3,4),(1,4,5),(1,6,3),(1,10,2),
                             (2,1,4),(2,5,6),(2,6,5),(2,8,2);
    SELECT * FROM #ddd;                         
    
    -- target x-values in table #vals (results are to go into column yy):
    CREATE TABLE #vals (xx float PRIMARY KEY,yy float null, itype int);
    INSERT INTO  #vals (xx) VALUES (1),(3),(4.3),(9),(12);
    
    -- do the actual interpolation
    WITH valstyp AS (
      SELECT id ii,xx,
             CASE WHEN min(x)<xx THEN CASE WHEN max(x)>xx THEN 1 ELSE 2 END ELSE 0 END flag,
             min(x) xmi,max(x) xma 
      FROM #vals INNER JOIN #ddd ON id=1 GROUP BY xx,id
    ), ipol AS (
      SELECT v.*,(b.x-xx)/(b.x-a.x) f,a.y ya,b.y yb 
      FROM valstyp v 
      INNER JOIN #ddd a ON a.id=ii AND a.x=(SELECT max(x) FROM #ddd WHERE id=ii 
                 AND (flag=0 AND x=xmi OR flag=1 AND x<xx OR flag=2 AND x<xma))
      INNER JOIN #ddd b ON b.id=ii AND b.x=(SELECT min(x) FROM #ddd WHERE id=ii 
                 AND (flag=0 AND x>xmi OR flag=1 AND x>xx OR flag=2 AND x=xma))
    )
    UPDATE v SET yy=ROUND(f*ya+(1-f)*yb,8),itype=flag FROM #vals v INNER JOIN ipol i ON i.xx=v.xx;
    
    -- list the interpolated results table:
    SELECT * FROM #vals
    

    运行上述脚本时,您将在表#ddd中获得以下数据网格点

    id x  y 
    -- -- - 
    1  3  4 
    1  4  5 
    1  6  3 
    1  10 2 
    2  1  4 
    2  5  6 
    2  6  5 
    2  8  2 
    

    [[ 该表包含两个身份(id=1id=2)的网格点。在我的示例中,我在 valstyp CTE 中使用 where id=1 仅引用了 1-group。这可以根据您的要求进行更改。 ]]

    结果表#vals 与列yy 中的插值数据:

    xx  yy   itype 
    --- ---- ----- 
    1   2    0     
    3   4    0     
    4.3 4.7  1     
    9   2.25 1     
    12  1.5  2     
    

    最后一列itype 表示用于计算值的插值/外插类型:

    0:  extrapolation to lower end
    1:  interpolation within given data range
    2:  extrapolation to higher end
    

    这个工作示例可以在here找到。

    【讨论】:

    • 赞成 5 年后回答的专用用户,谢谢 :)
    【解决方案3】:

    这里使用 CROSS JOIN 的技巧是,如果其中一个表没有行 (1 * 0 = 0) 并且查询可能会中断,则它不会返回任何记录。更好的方法是使用带有不等式条件的 FULL OUTER JOIN(以避免超过一行)

    ( SELECT TOP 1 
            Date, Rate 
        FROM Rates
        WHERE Date <= @InputDate
        ORDER BY Date DESC
      ) AS prev
      FULL OUTER JOIN
      ( SELECT TOP 1 
            Date, Rate 
        FROM Rates
        WHERE Date >= @InputDate
        ORDER BY Date ASC
      ) AS next
    ON (prev.Date <> next.Date) [or Rate depending on what is unique]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-21
      • 2013-02-15
      • 1970-01-01
      相关资源
      最近更新 更多