从 2 个表中查找每条记录的最接近日期匹配项

问题描述 投票:0回答:1

假设我有表1和表2,其中包含以下数据。我想找到表 1(JoiningDt) 中每个 ID 的 Table2.ClosestDt 中可用的下一个最接近的日期匹配项。注意:表 2 中的最接近匹配应大于表 1 的日期,并且 2 个 ID 不能具有相同的最接近匹配(例如:ID 2 应采用值 08-Apr-2024,即使 07-Apr-2024 是最接近的1,因为它已被 ID 1 占用。)

表1:

身份证 加入Dt 文档编号
1 2024 年 4 月 5 日 A123
2 2024 年 4 月 6 日 A123
3 2024 年 4 月 4 日 B123

表2

文档编号 最近Dt
A123 2024 年 4 月 3 日
A123 2024 年 4 月 4 日
A123 2024 年 4 月 7 日
A123 2024 年 4 月 8 日
B123 2024 年 4 月 2 日
B123 2024 年 4 月 5 日

我的预期输出是:

身份证 加入Dt 文档编号 最近Dt
1 2024 年 4 月 5 日 A123 2024 年 4 月 7 日
2 2024 年 4 月 6 日 A123 2024 年 4 月 8 日
3 2024 年 4 月 4 日 B123 2024 年 4 月 5 日

当我尝试左外连接时,我得到

身份证 加入Dt 文档编号 最近Dt
1 2024-04-05 A123 2024-04-07
1 2024-04-05 A123 2024-04-08
2 2024-04-06 A123 2024-04-07
2 2024-04-06 A123 2024-04-08
3 2024-04-04 B123 2024-04-05
select t1.ID ,t1.JoiningDt, t1.DocNum, (t2.ClosestDt)
from #Table1 t1
left join #Table2 t2 on
    t1.DocNum = t2.DocNum
    and t2.ClosestDt > t1.JoiningDt

我也尝试使用 rownumber,但具有挑战性的部分是获取 id 2 的下一场比赛(8-apr 而不是 7-apr),因为 7-apr 已被 Id 1 占用。

sql sql-server date closest
1个回答
0
投票

您可以使用相关子查询,您可以在其中选择最近的日期。

对于这样的动态,你需要至少处理两次数据。 基本思想是使用行号来确定两个日期选择的两个或多个齿的位置,然后根据其具有的行号简单地选择行中的下一个。

这意味着表 2 中有足够的数据来填充该列,否则您需要更多编程,以确定在最后一个日期之后接下来要采用哪些日期。

CREATE TABLE Table1 (
  ID INTEGER,
  JoiningDt DATETIME,
  DocNum VARCHAR(4)
);

INSERT INTO Table1
  (ID, JoiningDt, DocNum)
VALUES
  ('1', '05-Apr-2024', 'A123'),
  ('2', '06-Apr-2024', 'A123'),
  ('3', '04-Apr-2024', 'B123');

CREATE TABLE Table2 (
  DocNum VARCHAR(4),
  ClosestDt DATETIME
);

INSERT INTO Table2
  (DocNum, ClosestDt)
VALUES
  ('A123', '03-Apr-2024'),
  ('A123', '04-Apr-2024'),
  ('A123', '07-Apr-2024'),
  ('A123', '08-Apr-2024'),
  ('B123', '02-Apr-2024'),
  ('B123', '05-Apr-2024');
9 rows affected
WITH CTE1 As (
SELECT t1.ID, t1.JoiningDt, t1.DocNum,
  (SELECT TOP 1 ClosestDt FROM Table2 
  WHERE DocNum = t1.DocNum AND ClosestDt > t1.JoiningDt ORDER BY ClosestDt  ASC   ) ClosestDt
  , ROW_NUMBER() OVER(PARTITION BY DocNum,   (SELECT TOP 1 ClosestDt FROM Table2 
  WHERE DocNum = t1.DocNum AND ClosestDt > t1.JoiningDt ORDER BY ClosestDt  ASC   ) ORDER BY ID) rn
FROM Table1 t1
 )
SELECT ID, JoiningDt, DocNum, 
  CASE WHEN rn = 1 then ClosestDt ELSE
  (SELECT ClosestDt FROM Table2 
  WHERE DocNum = c1.DocNum AND ClosestDt > c1.JoiningDt ORDER BY ClosestDt  ASC   
  OFFSET c1.rn -1  ROWS FETCH NEXT 1 ROWS ONLY) END 
  FROM CTE1 c1
身份证 加入Dt 文档编号 (无栏名)
1 2024-04-05 00:00:00.000 A123 2024-04-07 00:00:00.000
2 2024-04-06 00:00:00.000 A123 2024-04-08 00:00:00.000
3 2024-04-04 00:00:00.000 B123 2024-04-05 00:00:00.000

小提琴

另一种方法有两个cte,这样子选择就不会运行两次,但它就像第一个一样,只是重写了

WITH CTE1 As (
SELECT t1.ID, t1.JoiningDt, t1.DocNum,
  (SELECT TOP 1 ClosestDt FROM Table2 
  WHERE DocNum = t1.DocNum AND ClosestDt > t1.JoiningDt ORDER BY ClosestDt  ASC   ) ClosestDt
FROM Table1 t1
 ), CTE2 AS (
  SELECT
       ID, JoiningDt, DocNum, ClosestDt
    , ROW_NUMBER() OVER(PARTITION BY DocNum,   ClosestDt ORDER BY ID) rn
  FROM CTE1
  )
SELECT ID, JoiningDt, DocNum, 
  CASE WHEN rn = 1 then ClosestDt ELSE
  (SELECT ClosestDt FROM Table2 
  WHERE DocNum = c1.DocNum AND ClosestDt > c1.JoiningDt ORDER BY ClosestDt  ASC   
  OFFSET c1.rn -1  ROWS FETCH NEXT 1 ROWS ONLY) END 
  FROM CTE2 c1
身份证 加入Dt 文档编号 (无栏名)
1 2024-04-05 00:00:00.000 A123 2024-04-07 00:00:00.000
2 2024-04-06 00:00:00.000 A123 2024-04-08 00:00:00.000
3 2024-04-04 00:00:00.000 B123 2024-04-05 00:00:00.000

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.