SQL: LEFT JOIN based on effective date period of another table

SQL: LEFT JOIN based on effective date period of another table

我有两个表:[transaction_table] (t) 和 [rate_table] (r)

我想 FROM [transaction_table] LEFT JOIN [rate_table] 根据 t.transaction_date 和 r.effective_date 以及乘积。
任何人都知道如何?提前致谢。

这是我的代码: 但它 returns 不希望的结果

SELECT t.*, r.rate 
FROM [transaction_table] t 
LEFT JOIN [rate_table] r on (t.product = r.product and t.transaction_date >= r.effective_date)

预期结果:交易 Table LEFT JOIN 率 Table,根据 effective_date

transaction_date 产品 金额 评分
2020-01-01 一个 200 0.2
2020-04-01 一个 200 0.3
2020-04-01 B 100 0.1
2021-01-01 一个 200 0.5

[Transaction_Table]:包含不同产品的所有交易

transaction_date 产品 金额
2020-01-01 一个 200
2020-04-01 一个 200
2020-04-01 B 100
2021-01-01 一个 200

[Rate_Table]:包含不同产品的费率调整,带有“effective_date”

effective_date 产品 评分
2019-01-01 一个 0.2
2019-01-01 B 0.1
2020-04-01 一个 0.3
2020-09-01 一个 0.5

您可以使用 APPLY 运算符通过 product 获取最新的 rate 并基于最新的 effective_date

SELECT t.*, r.rate 
FROM   [transaction_table] t 
       CROSS APPLY
       (
           SELECT TOP (1) r.rate
           FROM   [rate_table] r 
           WHERE  t.product          = r.product 
           AND    t.transaction_date >= r.effective_date
           ORDER BY r.effective_date DESC
       ) r

如果 rate_table

中可能存在不匹配的 rate,您可能还想使用 OUTER APPLY 而不是 CROSS APPLY

您在交易日期之前加入所有费率,而您只想获得最新的这些费率。您可以在 OUTER APPLY

中使用 TOP(1) 查询来实现此目的
select t.*, r.rate 
from transaction_table t 
outer apply
(
  select top(1) *
  from rate_table r 
  where r.product = t.product 
  and r.effective_date <= t.transaction_date
  order by r.effective_date desc
);

或在 SELECT 子句中的子查询中:

select
  t.*,
  (
    select top(1) r.rate 
    from rate_table r 
    where r.product = t.product 
    and r.effective_date <= t.transaction_date
    order by r.effective_date desc
  ) as rate
from transaction_table t;

您可以使用派生的 table,在其中通过使用 LEAD() 获取下一个生效日期来定义汇率的结束日期,即

SELECT  tt.transaction_date,
        tt.product,
        tt.amt,
        rt.rate
FROM    Transaction_Table AS tt
        LEFT JOIN
        (   SELECT  rt.effective_date, 
                    rt.product,
                    rt.rate,
                    end_date = LEAD(rt.effective_date) 
                                OVER(PARTITION BY rt.product ORDER BY rt.effective_date)
            FROM    rate_table AS rt
        ) AS rt
            ON rt.product = tt.product
            AND rt.effective_date <= tt.transaction_date
            AND (rt.end_date > tt.transaction_date OR rt.end_date IS NULL);

或者您可以使用 OUTER APPLYTOP 1,然后按 effective_date 订购以获得交易日期之前的最新汇率:

SELECT  tt.transaction_date,
        tt.product,
        tt.amt,
        rt.rate
FROM    Transaction_Table AS tt
        OUTER APPLY
        (   SELECT  TOP (1) rt.rate
            FROM    rate_table AS rt
            WHERE   rt.product = tt.product
            AND     rt.effective_date <= tt.transaction_date
            ORDER BY rt.effective_date DESC
        ) AS rt;

我通常会使用第一种方法来解决这个问题,因为您的费率 table 更有可能明显小于交易 table,但根据您的整体数据和索引,您可能会发现 OUTER APPLY 表现更好。

如果您要处理大量数据并且性能是一个问题,那么具体化您的速率 table 可能会有所帮助,例如

IF OBJECT_ID(N'tempdb..#rate', 'U') IS NOT NULL
    DROP TABLE #rate;
    
CREATE TABLE #rate
(
        Product CHAR(1) NOT NULL, --Change type as necessary
        FromDate DATE NOT NULL,
        ToDate DATE NULL,
        Rate DECIMAL(10, 2) NOT NULL, -- Change type as necessary
    PRIMARY KEY (Product, FromDate)
);
INSERT #rate(Product, FromDate, ToDate, Rate)
SELECT  rt.product,
        rt.effective_date, 
        end_date = LEAD(rt.effective_date) 
                    OVER(PARTITION BY rt.product ORDER BY rt.effective_date),
        rt.rate
FROM    rate_table AS rt;


SELECT  tt.transaction_date,
        tt.product,
        tt.amt,
        rt.rate
FROM    Transaction_Table AS tt
        LEFT JOIN #rate AS rt
            ON rt.product = tt.product
            AND rt.FromDate <= tt.transaction_date
            AND (rt.ToDate > tt.transaction_date OR rt.ToDate IS NULL);