我想按日期联接两个表(A和B)。由于日期不一定在表之间整齐地排列,这使情况变得复杂。也就是说,表B中的日期可能不在表A中,也可能不在表A中。
我怀疑必须有一种简单的方法可以在SQL / SAS中完成此操作,但是我对这两者都是陌生的,并且不知道如何做。如果有人可以指出具体的解决方案,示例或可以用来解决此问题的功能,我将感到非常高兴。我在下面创建了一个虚拟案例进行说明。
这是一个表(对于一个参与者)的外观示例:
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的范围内选择一个条目,如果表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中的数据之间的日期差。最后,对于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数据,而只是在结束日期中获得最接近的数据。
答案 0 :(得分:0)
我认为您可以将现有逻辑简化为一个查询:
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) )
如果结果表与您要生成的表不同,请说明。
答案 1 :(得分:0)
当您必须处理领带,最大覆盖率与水趾重叠时,日期范围联接可能会非常复杂……您当然不希望在最终解决方案中存储工会和中介人,尽管它们在调试逻辑时会有所帮助。
这是一种相关的子查询技术,用于查找与A匹配的“最佳”内容范围。如果内容数据与end_date
中的participant
不相同,则会出现问题。
每个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;