根据不匹配的日期字段连接两个表
Posted
技术标签:
【中文标题】根据不匹配的日期字段连接两个表【英文标题】:Joining two tables based on non-matching date fields 【发布时间】:2019-02-19 11:42:00 【问题描述】:我想按日期加入两个表(A 和 B)。由于日期不一定在表格中整齐排列,这一事实使情况变得复杂。也就是说,表 B 中的日期可能落在表 A 的条目之外或之间。
我怀疑在 SQL/SAS 中一定有一种简单的方法可以实现这一点,但我对这两种方法都很陌生,不知道怎么做。如果有人能指出我可以用来解决这个问题的具体解决方案、示例或功能,我将非常高兴。我在下面创建了一个虚构的案例来说明。
这是表 A 的外观示例(对于一位参与者):
Table A
-------------------------------------------+
participant start end
-------------------------------------------+
101 1-1-2010 26-4-2010
101 27-4-2010 2-10-2014
101 3-10-2014 4-1-2015
101 5-1-2015 31-8-2015
101 1-9-2015 12-10-2016
101 13-10-2016 31-12-2018
下面是需要连接到表 A 的表 B 的示例。如您所见,对于简单的左连接,日期差异太大:
Table B
---------------------------------------------------------+
participant start_date end_date Content
---------------------------------------------------------+
101 1-1-2012 31-8-2012 A
101 1-9-2012 31-8-2013 B
101 1-9-2013 31-8-2014 C
101 1-9-2014 2-10-2014 D
101 3-10-2014 31-8-2015 E
101 1-9-2015 31-1-2016 F
101 1-9-2015 31-1-2016 F
连接表 C 的想法是表 A 的每一行都由表 B 中的数据通知。我想从 B 中选择一个位于表 A 范围内的条目。如果 B 中的多个条目适合,则应该使用最近的一个。如果表 B 没有该时期的信息(如第一行的情况),则应使用最接近的信息。另一种说法是,我希望将 B 的最新信息添加到 A 的每一行。
Table C
----------------------------------------------------------------------+
participant startA endA startB endB Content
----------------------------------------------------------------------+
101 1-1-2010 26-4-2010 1-1-2012 31-8-2012 A
101 27-4-2010 2-10-2014 1-9-2013 31-8-2014 C
101 3-10-2014 4-1-2015 1-9-2014 2-10-2014 D
101 5-1-2015 31-8-2015 3-10-2014 31-8-2015 E
101 1-9-2015 12-10-2016 1-9-2015 31-1-2016 F
101 13-10-2016 31-12-2018 1-9-2015 31-1-2016 F
这是我第一次使用 SAS 和 SQL,所以我自己的努力效果很差。下面,我通过几个步骤将这两个表连接到一个过程中:我首先创建一个完整连接以获取表 A 和 B 的所有可能(相关)排列。然后我计算表 A 中数据之间的日期差异和 B. 最后,对于 A 的每个时期,我选择原始表中的数据之间的日期差异最小的行。
/* Create outer join of both tables*/
PROC SQL;
CREATE TABLE work.fulljoin AS
SELECT a.*, b.*
FROM work.table_A AS a
FULL JOIN work.table_B AS b ON a.participant = b.participant;
quit;
/* Group by ID and entry date of each period */
PROC SORT data=work.fulljoin;
BY participant startA;
RUN;
/* Calculate the date differences between tables A and B */
DATA work.fulljoin_wdelta;
SET work.fulljoin;
delta=abs(endA-endB);
RUN;
/* Remove unnecessary rows */
PROC SQL;
CREATE TABLE output.joined AS
SELECT * FROM work.fulljoin_wdelta
GROUP BY participant, startA
HAVING delta=min(delta);
QUIT;
但是,对于大型数据集(A 和 B 中的数百万行),这变得令人望而却步。此外,严格来说,此方法并不强制您将获取每个 A 周期的最新 B 数据,而只是获取结束日期最接近的 B 数据。
【问题讨论】:
您的表格 C 是您希望连接表格的外观示例,还是您迄今为止设法制作的最佳示例?如果是后者,请根据您的输入表 A 和 B 提供一个示例,说明您希望它的外观。 数百万行是 SAS 数据集还是远程数据库表?如果有,数据集上有哪些索引? 感谢您的 cmets! @user667489,表 C 是我想要摆脱的。理查德,这是关于 SAS 数据集的。 【参考方案1】:当您必须处理关系时,日期范围连接可能会非常复杂,最大覆盖范围与水中脚趾重叠等...您当然不想将联合和中介存储在最终解决方案中,尽管它们在调试逻辑时会有所帮助。
这是一种相关子查询技术,用于查找匹配 A 的“最佳”内容范围。如果内容数据与 participant
中的 end_date
不同,则会出现问题。
每个one
行(目标)都已完成查找。范围重叠逻辑很重要
where one.participant = two.participant
and two.start_date < one.end
and two.end_date > one.start
并允许内容日期范围部分超出目标范围。
data one;
input participant start: ddmmyy. end: ddmmyy.;
format start end yymmdd10.;
datalines;
101 1-1-2010 26-4-2010
101 27-4-2010 2-10-2014
101 3-10-2014 4-1-2015
101 5-1-2015 31-8-2015
101 1-9-2015 12-10-2016
101 13-10-2016 31-12-2018
;
data two;
input participant start_date: ddmmyy. end_date: ddmmyy. Content: $;
format start_date end_date yymmdd10.;
datalines;
101 1-1-2012 31-8-2012 A
101 1-9-2012 31-8-2013 B
101 1-9-2013 31-8-2014 C
101 1-9-2014 2-10-2014 D
101 3-10-2014 31-8-2015 E
101 1-9-2015 31-1-2016 F
101 1-9-2015 31-1-2017 F
run;
proc sql;
create table want as
select
one.*
, ( select min(content)
from two
where one.participant = two.participant
and two.start_date < one.end
and two.end_date > one.start
group by participant
having end_date = max(end_date)
) as content
from
one
order by
participant, start
;
quit;
【讨论】:
这看起来很酷!范围查找逻辑确实很棘手。你能解释一下 min(content) 是否很重要吗?似乎在 ONE 中每行选择 TWO 中最旧的条目;如果我离开 min() ,它会得到我更喜欢的最新数据。这就引出了关于这段代码的第二个问题。真正的“TWO”表有很多列。我无法使用此代码选择多个。理想情况下,我想声明“SELECT * FROM two”,但随后在“ORDER BY 参与者,开始”行中出现关于复合表达式的错误。这有什么诀窍吗? 当关联子查询匹配多于一行时会报错,因此子查询被分组,当有两个或多个@时返回最低值content
987654327@ 具有相同最高 end_date
的行。在 它会有问题 中进行了修饰。您对使用这种查询方法的探索可能会发现高资源使用率(cpu/磁盘/时间)和寻求更多替代方案的愿望。
SAS SQL 相关子查询 (CSQ) 只能使用 1 元组(一列)提供(某些数据库可能允许 n 元组(CSQ 中的多个选择))并且您有看到它不适合替换从two
中选择多个列的更传统的连接。对所有日期范围匹配条件进行稳健处理的查询并非易事。
有道理,感谢您的解释!不知道元组限制。我想我要做的是将流程分成几部分 - 使用您的代码从表 2 中附加正确的日期并将它们存储在表 1 中。在第二步中,我应该能够根据新的日期列左加入表 2 的其余部分。我要上班了!【参考方案2】:
我认为您可以将现有逻辑简化为单个查询:
proc sql noprint _method;
create table table_c as
select
a.participant,
a.start as start_a,
a.end as end_a,
b.start_date as start_b,
b.end_date as end_b,
abs(a.end - b.end_date) as delta
from table_a a inner join table_b b
on a.participant = b.participant
group by a.participant, start_a
having delta = min(delta)
;
quit;
只要你有足够的内存,日志输出就会确认这会执行哈希连接:
NOTE: SQL execution methods chosen are:
sqxcrta
sqxsumg
sqxsort
sqxjhsh
sqxsrc( WORK.TABLE_B(alias = B) )
sqxsrc( WORK.TABLE_A(alias = A) )
如果生成的表格与您尝试生成的表格不同,请澄清。
【讨论】:
谢谢,我还在做很多小步骤,因为我已经习惯了 SQL/SAS。这确实更优雅。以上是关于根据不匹配的日期字段连接两个表的主要内容,如果未能解决你的问题,请参考以下文章