嗨,另一个快速问题
在我们用于条件连接的proc sql中是否有类似的sas数据步骤
例如
proc sql;
....
data1 left join data2
on first<value<last
quit;
我们可以在 sas datastep中复制它吗
喜欢
data work.combined
set data1(in=a) data2(in=b)
if a then output;
run;
您还可以使用哈希对象在一个数据步骤中重现 sql 连接。它可能非常快,但取决于您机器的 RAM 大小,因为此方法将一个表加载到内存中。因此,RAM 越多 - 您可以包装成散列的数据集就越大。这种方法对于在相对较小的参考表中查找特别有效。
data have1;
input first last;
datalines;
1 3
4 7
6 9
;
run;
data have2;
input value;
datalines;
2
5
6
7
;
run;
data want;
if _N_=1 then do;
if 0 then set have2;
declare hash h(dataset:'have2');
h.defineKey('value');
h.defineData('value');
h.defineDone();
declare hiter hi('h');
end;
set have1;
rc=hi.first();
do while(rc=0);
if first<value<last then output;
rc=hi.next();
end;
drop rc;
run;
结果:
value first last
2 1 3
5 4 7
6 4 7
7 6 9
是的,只需 7 行代码就有一个简单(但微妙)的方法。
您打算实现的本质上是条件笛卡尔连接,可以通过 do-looped set 语句完成。以下代码使用来自 Dmitry 的测试数据集和SUGI Paper 249-30附录中代码的修改版本
data data1;
input first last;
datalines;
1 3
4 7
6 9
;
run;
data data2;
input value;
datalines;
2
5
6
7
;
run;
/***** by data step looped SET *****/
DATA CART_data;
SET data1;
DO i=1 TO NN; /*NN can be referenced before set*/
SET data2 point=i nobs=NN; /*point=i - random access*/
if first<value<last then OUTPUT; /*conditional output*/
END;
RUN;
/***** by SQL *****/
proc sql;
create table cart_SQL as
select * from data1
left join data2
on first<value<last;
quit;
很容易看出结果是一致的。
另请注意,来自 SAS 9.2文档:“在编译时,SAS 读取每个数据集的描述符部分并自动分配 NOBS= 变量的值。因此,您可以在 SET 语句之前引用 NOBS= 变量。变量在 DATA 步中可用,但不会添加到任何输出数据集中。”
这是另一种解决方案,使用临时数组来保存查找数据集。性能可能类似于 Dmitry 的基于散列的解决方案,但这对于仍在使用 9.1 之前的 SAS 版本(即首次引入散列对象时)的人也应该有效。
我重用了 Dmitry 的示例数据集:
data have1;
input first last;
datalines;
1 3
4 7
6 9
;
run;
data have2;
input value;
datalines;
2
5
6
7
;
run;
/*We need a macro var with the number of obs in the lookup dataset*/
/*This is so we can specify the dimension for the array to hold it*/
data _null_;
if 0 then set have2 nobs = nobs;
call symput('have2_nobs',put(nobs,8.));
stop;
run;
data want_temparray;
array v{&have2_nobs} _temporary_;
do _n_ = 1 to &have2_nobs;
set have2 (rename=(value=value_array));
v{_n_}=value_array;
end;
do _n_ = 1 by 1 until (eof_have1);
set have1 end = eof_have1;
value=.;
do i=1 to &have2_nobs;
if first < v{i} < last then do;
value=v{i};
output;
end;
end;
if missing(value) then output;
end;
drop i value_array;
run;
输出:
value first last
2 1 3
5 4 7
6 4 7
7 6 9
这与等效 SQL 的输出相匹配:
proc sql;
create table want_sql as
select * from
have1 left join have2
on first<value<last
;
quit;
run;
使用 MERGE 没有直接的方法来做到这一点。这是 SQL 方法明显优于任何 SAS 数据步骤方法的一个示例,因为您所做的任何事情都将花费更多的代码并且可能需要更多的时间。
但是,根据数据,一些方法可能是有意义的。特别是格式合并。
如果data1
它相当小(甚至数百万条记录),您可以用它制作格式。像这样:
data fmt_set;
set data1;
format label $8.;
start=first; *set up the names correctly;
end=last;
label='MATCH';
fmtname='DATA1F';
output;
if _n_=1 then do; *put out a hlo='o' line which is for unmatched lines;
start=.; *both unnecessary but nice for clarity;
end=.;
label='NOMATCH';
hlo='o';
output;
end;
run;
proc format cntlin=fmt_set; *import the dataset;
quit;
data want;
set data2;
if put(value,DATA1F.)="MATCH";
run;
这运行起来非常快,除非data1
非常大(在我的系统上数亿行) - 如果您包括排序时间,则比数据步骤合并更快,因为这不需要排序。一个主要限制是,这只会给你每data2
行一行;如果这是所需要的,那么这将起作用。如果你想要重复,data2
那么你不能这样做。
如果data1
可能有重叠的行(即,开始/结束相互重叠的两行),您还需要解决这个问题,因为开始/结束不允许正常重叠。您可以hlo="m"
为每一行设置,为不匹配的行设置“om”,或者您可以解决重叠问题。
但是,我仍然会进行 sql 连接,因为它的代码更短且更易于阅读,除非您遇到性能问题,或者它无法按照您希望的方式工作。