SQL Join on Nearest 小于日期

Posted

技术标签:

【中文标题】SQL Join on Nearest 小于日期【英文标题】:SQL Join on Nearest less than date 【发布时间】:2010-01-26 21:49:58 【问题描述】:

通常我会在代码本身中执行此操作,但我很好奇这是否可以在 TSQL 中有效地完成。

表1
日期 - 值
表2
日期 - 折扣

表 1 包含每天的条目。表 2 仅包含折扣更改时的条目。在输入新的折扣之前,应用到某个值的折扣被视为有效。

示例数据:

表1
2010 年 1 月 26 日 - 10 日
2010 年 1 月 25 日 - 9 日
2010 年 1 月 24 日 - 8 日
2010 年 1 月 24 日 - 9 日
2010 年 1 月 23 日 - 7
2010 年 1 月 22 日 - 10 日
2010 年 1 月 21 日 - 11 日
表 2 2010 年 1 月 26 日 - 2 2010 年 1 月 23 日 - 1 2010 年 1 月 20 日 - 0

我需要返回的内容如下:T1 Date - T1 Value - T2 Discount

示例数据:

2010 年 1 月 26 日 - 10 - 2 2010 年 1 月 25 日 - 9 - 1 2010 年 1 月 24 日 - 8 - 1 2010 年 1 月 24 日 - 9 - 1 2010 年 1 月 23 日 - 7 - 1 2010 年 1 月 22 日 - 10 - 0 2010 年 1 月 21 日 - 11 - 0

可能还是我最好继续在代码中执行此操作?

【问题讨论】:

【参考方案1】:

我相信这个子查询会做到(未经测试)。

select *, 
   (select top 1 Discount 
    from table2 
    where table2.Date <= t.Date 
    order by table2.Date desc) as Discount
from Table1 t

但也许不是性能最好的。

编辑:

测试代码:

create table #table1 ([date] datetime, val int)
create table #table2 ([date] datetime, discount int)

insert into #table1 ([date], val) values ('1/26/2010', 10)
insert into #table1 ([date], val) values ('1/25/2010', 9)
insert into #table1 ([date], val) values ('1/24/2010', 8)
insert into #table1 ([date], val) values ('1/24/2010', 9)
insert into #table1 ([date], val) values ('1/23/2010', 7)
insert into #table1 ([date], val) values ('1/22/2010', 10)
insert into #table1 ([date], val) values ('1/21/2010', 11)

insert into #table2 ([date], discount) values ('1/26/2010', 2)
insert into #table2 ([date], discount) values ('1/23/2010', 1)
insert into #table2 ([date], discount) values ('1/20/2010', 0)

select *, 
   (select top 1 discount 
    from #table2 
    where #table2.[date] <= t.[date]
    order by #table2.[date] desc) as discount
from #table1 t

drop table #table1
drop table #table2

结果:

2010-01-26 00:00:00.000 10 2 2010-01-25 00:00:00.000 9 1 2010-01-24 00:00:00.000 8 1 2010-01-24 00:00:00.000 9 1 2010-01-23 00:00:00.000 7 1 2010-01-22 00:00:00.000 10 0 2010-01-21 00:00:00.000 11 0

【讨论】:

【参考方案2】:

没有“最近”查询会像“等于”查询那样高效,但这是值得信赖的ROW_NUMBER 的另一项工作:

;WITH Discounts_CTE AS
(
    SELECT
        t1.[Date], t1.[Value], t2.Discount,
        ROW_NUMBER() OVER
        (
            PARTITION BY t1.[Date]
            ORDER BY t2.[Date] DESC
        ) AS RowNum
    FROM Table1 t1
    INNER JOIN Table2 t2
        ON t2.[Date] <= t1.[Date]
)
SELECT *
FROM Discounts_CTE
WHERE RowNum = 1

【讨论】:

+1:好主意。看看子查询与使用 row_number 连接的基准标记会很有趣。 在我的例子中,使用 SQLite 和 ~2000 行,子查询快 10 倍以上【参考方案3】:

添加到 Joels 的答案...如果您在两个表中都有 ID,则以下内容将提高性能:

select *, 
   (select top 1 Discount 
    from Table2 t2
    where t2.Date <= t1.Date 
    and t2.ID = t1.ID 
    order by t2.Date desc) as Discount
from Table1 t1

【讨论】:

“Joels Answer”可能会改变或消失。写下你的答案以独立。 @HackSlash (1) “joels answer”是公认的答案,它极不可能消失;参考别人的答案并不少见。 (2) 答案是独立的……? 如果您想添加到 Joel 的答案中,只需在他的答案上单击“编辑”并添加即可。 Stack Overflow 是一个维基。您可以编辑其他人的问题和答案。【参考方案4】:

这是asof join 的典型场景。在DolphinDB,可以直接使用asof join来有效解决这个问题。

测试代码:

table1 = table(2010.01.26 2010.01.25 2010.01.24 2010.01.24 2010.01.23 2010.01.22 2010.01.21 as date,  10 9 8 9 7 10 11 as val)
table2 = table(2010.01.26 2010.01.23 2010.01.20 as date, 2 1 0 as discount)
select date, val, discount from aj(table1, (select * from table2 order by date), `date)

【讨论】:

【参考方案5】:

这适用于 oracle XE。由于sql server确实有解析功能,所以移植应该不难。

create table one (
    day date,
    value integer
);


create table two (
    day date,
    discount integer
);


insert into one values (trunc(sysdate), 10);
insert into one values (trunc(sysdate-1), 8);
insert into one values (trunc(sysdate-2), 1);
insert into one values (trunc(sysdate-3), 23);
insert into one values (trunc(sysdate-4), 3);
insert into one values (trunc(sysdate-5), 4);
insert into one values (trunc(sysdate-6), 8);
insert into one values (trunc(sysdate-7), 5);
insert into one values (trunc(sysdate-8),8);
insert into one values (trunc(sysdate-9), 8);
insert into one values (trunc(sysdate-10), 5);    


insert into two values (trunc(sysdate), 2);
insert into two values (trunc(sysdate-3), 1);
insert into two values (trunc(sysdate-5), 3);
insert into two values (trunc(sysdate-8), 1);


select day, value, discount, cnt,
    nvl(max(discount) over (partition by cnt) 
    ,0) as calc_discount
from (
    select day, value, discount,
        count(discount) over (order by day) as cnt
    from one
    left outer join two  
    using(day) 
)

【讨论】:

以上是关于SQL Join on Nearest 小于日期的主要内容,如果未能解决你的问题,请参考以下文章

求助sql语句,多个join嵌套

SQL中join时如何限制行数上限?

sql中join on两个条件怎么写

SQL语句中的join只能与on连用吗?有没有只用join的?举个例子行吗?

SQL中left join on 、right join on、inner join on之间的区别

sql语法:inner join on, left join on, right join on详细使用方法