我最近继承了一个看起来像这样的SAS程序:
%MACRO ComplicatedStuff( GroupId= );
%LET FileId = %SYSFUNC( OPEN( Work.BigDataSet ) );
%PUT 'Doing something really difficult with ' &GroupId.;
%LET CloseRC = %SYSFUNC( CLOSE( &FileId. ) );
%MEND ComplicatedStuff;
%ComplicatedStuff(GroupId=ABC1);
%ComplicatedStuff(GroupId=DEF2);
%ComplicatedStuff(GroupId=3GHI);
%ComplicatedStuff(GroupId=J4KI);
Run Code Online (Sandbox Code Playgroud)
作为一个多方面的程序员,我看着这个并且想"我肯定能让这个变得更有活力".果然,我能够使用以下方法开发我认为简单的解决方案CALL EXECUTE:
DATA Work.IDs;
INPUT ID $4.
;
DATALINES;
ABC1
DEF2
3GHI
J4KI
RUN;
DATA Work.CommandDebug;
SET Work.IDs;
Command = CATS(
'%ComplicatedStuff(GroupId=', ID, ');'
);
CALL EXECUTE( Command );
RUN;
Run Code Online (Sandbox Code Playgroud)
我很满意这个解决方案,直到将ComplicatedStuff生成的文件FTP到另一台服务器.我们的SAS服务器在Unix上运行,SAS管理员为我们创建了一个有用的小宏来调用named %sas_sftp(因为,我被告知,x代码变得非常难看).不幸的是,我无法发布%sas_sftp代码 - 它属于我的公司,我不认为他们想要它.
我尝试像调用%sas_sftp宏一样调用%ComplicatedStuff宏(CALL EXECUTE在同一数据步骤中作为第二个,并作为第二个数据步骤),但只有第一个文件(大约30个)才能到达目的地.当我查看日志时,看起来第二个宏在ftp完成之前开始执行(ftp管道,或者不管它是什么,在下一个ftp启动之前还没有释放),所以后续的ftps只是默默地失败了由于资源不可用(我推测).
我认为EXECUTE基本上会排队我的宏调用,然后执行它们就像它们按顺序位于代码中一样(就像它们原来的那样) - 一次一个.很明显其他事情正在发生,因为虽然上面的第一种方法没有问题,但我的动态解决方案失败了.我倾注了CALL EXECUTE:How and Why和SAS文档,但我担心我只是不明白他们在谈论什么.
我最终找到了一个工作(或者更确切地说,一个同事找到了一个),我在下面发布了一个"答案",但我真的很想有人解释EXECUTE函数及其工作原理.
为什么我的第一次尝试,使用CALL EXECUTE,工作?
CALL EXECUTE 的工作方式与社区 wiki 中的代码类似,但与计时相关的一些特定问题除外。我遇到的最常见的问题是当我正在做一个包含定义宏变量的宏时,例如宏PROC SQL select into内部的宏,然后创建宏中使用的宏文本 - 与您的答案没有什么不同。由于计时规则的原因,直到 CALL EXECUTE 完成构建要执行的代码之后才会执行该操作,这意味着代码内部的值不会正确更改。
这是一个例子。
%macro mymacro(age=0);
proc sql noprint;
select quote(name) into :namelist separated by ',' from sashelp.class where age=&age.;
quit;
data name_age;
set sashelp.class;
where name in (&namelist.);
run;
proc print data=name_age;
var name age;
run;
%mend mymacro;
proc sort data=sashelp.class out=class nodupkey;
by age;
run;
Run Code Online (Sandbox Code Playgroud)
好的,现在我有一个控制数据集 ( class) 和一个运行它的宏。这里是call execute。这不能正常工作;第一次运行时,您将收到有关 &namelist 未定义的消息,第二次和以后您将收到所有age=16(最后一个年龄),因为这就是宏变量的定义。
data _null_;
set class;
exec_val = cats('%mymacro(age=',age,')');
call execute(exec_val);
run;
Run Code Online (Sandbox Code Playgroud)
这是sql宏调用。这按预期工作。
proc sql noprint;
select cats('%mymacro(age=',age,')') into :calllist separated by ' '
from class;
quit;
&calllist;
Run Code Online (Sandbox Code Playgroud)
对于数据生成的代码,我认为调用执行不如 PROC SQL 宏列表解决方案那么有用,除非在数据步骤中构造代码更容易并且我没有做任何导致计时问题的事情。