根据最近的最新日期,在2个表之间匹配值的最有效方法是什么?

时间:2014-05-22 16:34:19

标签: sql sql-server tsql date

我在MS SQL Server中有两个表:

dailyt - 其中包含每日数据:

date             val
---------------------
2014-05-22       10
2014-05-21       9.5
2014-05-20       9
2014-05-19       8
2014-05-18       7.5
etc...

periodt - 包含不定期的数据:

date             val
---------------------
2014-05-21       2
2014-05-18       1

如果dailyt中有一行,我想通过在periodt中添加相应的值来调整其值,其中最接近的日期先于或等于dailyt行的日期。因此,输出看起来像:

addt

date             val
---------------------
2014-05-22       12      <- add 2 from 2014-05-21
2014-05-21       11.5    <- add 2 from 2014-05-21
2014-05-20       10      <- add 1 from 2014-05-18
2014-05-19       9       <- add 1 from 2014-05-18
2014-05-18       8.5     <- add 1 from 2014-05-18

我知道这样做的一种方法是加入dailyt上的periodtperiodt.date <= dailyt.date表,然后强加ROW_NUMBER() (PARTITION BY dailyt.date ORDER BY periodt.date DESC)条件,然后加WHERE 1}}行号上的条件为= 1。

还有另一种方法可以提高效率吗?或者这是非常优化的?

2 个答案:

答案 0 :(得分:4)

我认为使用APPLY将是最有效的方式:

SELECT  d.Val,
        p.Val,
        NewVal = d.Val + ISNULL(p.Val, 0)
FROM    Dailyt AS d
        OUTER APPLY
        (   SELECT  TOP 1 Val
            FROM    Periodt p
            WHERE   p.Date <= d.Date
            ORDER BY p.Date DESC
        ) AS p;

<强> Example on SQL Fiddle

答案 1 :(得分:0)

如果行periodt行相对较少,那么有一个选项可能非常有效。

使用子查询或CTE将periodt转换为From / To范围表。 (显然,性能取决于这个初始步骤的执行效率,这就是为什么少数periodt行更可取。)然后加入dailyt将非常高效。 E.g。

;WITH PIds AS (
        SELECT ROW_NUMBER() OVER(ORDER BY PDate) RN, *
        FROM   @periodt
    ),
    PRange AS (
        SELECT  f.PDate AS FromDate, t.PDate as ToDate, f.PVal
        FROM    PIds f
                LEFT OUTER JOIN PIds t ON
                  t.RN = f.RN + 1
    )
SELECT  d.*, p.PVal
FROM    @dailyt d
        LEFT OUTER JOIN PRange p ON
            d.DDate >= p.FromDate
        AND (d.DDate < p.ToDate OR p.ToDate IS NULL)
ORDER BY 1 DESC

如果要尝试查询,以下内容将使用表变量生成示例数据。注意我向dailyt添加了一行,以演示日期较小的periodt条目。

DECLARE  @dailyt table (
    DDate date NOT NULL,
    DVal float NOT NULL
)

INSERT INTO @dailyt(DDate, DVal)
          SELECT '20140522', 10
UNION ALL SELECT '20140521', 9.5
UNION ALL SELECT '20140520', 9
UNION ALL SELECT '20140519', 8
UNION ALL SELECT '20140518', 7.5
UNION ALL SELECT '20140517', 6.5

DECLARE @periodt table (
    PDate date NOT NULL,
    PVal int NOT NULL
)

INSERT INTO @periodt
          SELECT '20140521', 2
UNION ALL SELECT '20140518', 1