SQL:LEFT JOIN 基于另一个表的生效日期

Posted

技术标签:

【中文标题】SQL:LEFT JOIN 基于另一个表的生效日期【英文标题】:SQL: LEFT JOIN based on effective date period of another table 【发布时间】:2021-06-02 06:52:26 【问题描述】:

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

我想根据 t.transaction_date 和 r.effective_date 和产品 FROM [transaction_table] LEFT JOIN [rate_table]。 有谁知道怎么做?提前致谢。

这是我的代码: 但它会返回不想要的结果

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)

Desired Outcome: Transaction Table LEFT JOIN Rate Table, rate 根据有效日期

transaction_date product amt rate
2020-01-01 A 200 0.2
2020-04-01 A 200 0.3
2020-04-01 B 100 0.1
2021-01-01 A 200 0.5

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

transaction_date product amt
2020-01-01 A 200
2020-04-01 A 200
2020-04-01 B 100
2021-01-01 A 200

[Rate_Table]:包含具有“有效日期”的不同产品的费率调整

effective_date product rate
2019-01-01 A 0.2
2019-01-01 B 0.1
2020-04-01 A 0.3
2020-09-01 A 0.5

【问题讨论】:

【参考方案1】:

您在交易日期之前加入所有费率,而您只想获得最新的。您可以通过 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;

【讨论】:

【参考方案2】:

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

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

【讨论】:

【参考方案3】:

您可以使用派生表,通过使用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;

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

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

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);

【讨论】:

以上是关于SQL:LEFT JOIN 基于另一个表的生效日期的主要内容,如果未能解决你的问题,请参考以下文章

SQL 在 LEFT JOIN 中获取 MAX 日期时间

SQL语句(inner join,left out join,right out join三者的不同

记一次sql优优化——left join不走索引问题

sql语句中join、left join 、right join有啥区别?

SQL语句中LEFT JOIN和RIGHT JOIN 以及INNER JOIN的区别

LEFT JOIN用法,好久没用过原生sql了