为什么直接运行时使用CALL EXECUTE运行宏错误?

时间:2013-07-10 21:16:54

标签: sas

我最近继承了一个看起来像这样的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);

作为一个多方面的程序员,我看着这个并且想“我肯定能让这个变得更有活力”。果然,我能够使用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;

我很满意这个解决方案,直到将ComplicatedStuff生成的文件FTP到另一台服务器。我们的SAS服务器在Unix上运行,SAS管理员为我们创建了一个有用的小宏来调用名为%sas_sftp(因为,我被告知,x代码变得非常丑陋)。不幸的是,我无法发布%sas_sftp代码 - 它属于我的公司,我认为他们不想要它。

我尝试调用%sas_sftp宏,就像调用%ComplicatedStuff宏一样(在同一数据步骤中作为第二个CALL EXECUTE,并作为第二个数据步骤),但只有第一个文件(大约30个)会到达目的地。当我查看日志时,看起来第二个宏在ftp完成之前开始执行(ftp管道,或者不管它是什么,在下一个ftp启动之前还没有释放),所以后续的ftps只是默默地失败了由于资源不可用(我推测)。

我认为EXECUTE基本上会将我的宏调用排队,然后执行它们就好像它们按顺序位于代码中一样(就像它们原来的那样) - 一次一个。很明显其他事情正在发生,因为虽然上面的第一种方法没有问题,但我的动态解决方案失败了。我倾注了CALL EXECUTE: How and WhySAS documentation,但我恐怕只是不明白他们在谈论什么。

我最终找到了一个工作(或者更确切地说,一个同事找到了一个),我在下面发布了一个“答案”,但我真的很想有人解释EXECUTE功能以及它是如何工作的。

为什么我的第一次尝试使用CALL EXECUTE无效?

3 个答案:

答案 0 :(得分:4)

这是一个避免EXECUTE功能的工作。我发布它作为对未来访问者的帮助,但它并没有真正回答我的核心问题。

下面的代码通过利用SQL INTO:语法创建我想要执行的命令的宏变量。然后我创建一个简单的宏,它基本上迭代宏变量并解析它们(导致语句执行就好像它们在源代码中一样)。

PROC SQL NOPRINT;
    SELECT      COUNT(*)
    INTO        :CommandCount
    FROM        Work.CommandDebug
    ;

    SELECT      Command
    INTO        :Command1 - :Command%LEFT(&CommandCount.)
    FROM        Work.CommandDebug
    ;
QUIT;

%MACRO ExeCommands;

    %DO I = 1 %TO &CommandCount.;

        &&Command&I.; /* Resolves to %ComplicatedStuff(GroupId=ABC1);, etc */

    %END;

%MEND;
%ExeCommands;

答案 1 :(得分:4)

CALL EXECUTE与社区维基中的代码类似,除了与时间相关的一些特定问题。我遇到的最常见的问题是当我在做一个包含定义宏变量的宏的宏时,例如宏中的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;

好的,现在我有一个控制数据集(class)和一个用来运行它的宏。这是call execute。这不能正常工作;第一次运行时,您将收到有关& namelist未定义的消息,第二次和将来您将获得所有年龄= 16(最后一个年龄),因为这是宏变量定义为。

data _null_;
set class;
exec_val = cats('%mymacro(age=',age,')');
call execute(exec_val);
run;

这是sql宏调用。这可以按预期工作。

proc sql noprint;
select cats('%mymacro(age=',age,')') into :calllist separated by ' '
    from class;
quit;   
&calllist;

我没有发现调用execute对于数据生成代码的PROC SQL宏列表解决方案有用,除非在数据步骤中构造代码更容易,并且我没有做任何导致时序问题的事情。

答案 2 :(得分:1)

在CALL EXECUTE中进行宏评估计时的一个好方法是将代码放入临时文件中,然后包含该文件。当您想要提交已提交代码的硬拷贝时(仅将fileref切换为物理文件),这对于调试也很有用。

%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;

filename blah temp;

data _null_;
set class;
file blah;
exec_val = cats('%mymacro(age=',age,')');
put exec_val;
run;

%include blah;

filename blah clear;