【问题标题】:Dynamic variable in a call execute-macro script in SASSAS中调用执行宏脚本中的动态变量
【发布时间】:2017-11-08 08:58:10
【问题描述】:

我有一个宏,它将生成一个包含 prod 列和另外 12 个列的数据集。我还有一个表 prodincl_fr,其中包含下面的示例数据集。

Rownum prodcat
----------
1      L
2      L1
3      M
4      LM
...    ...

我想做一个 do until 循环,它将调用宏 %runlimitsquery 并在每次迭代中使用 prodcat 的值。

我不知道如何在 SAS 中编写这个代码。我正在玩下面的代码。注意reccount=prodincl_fr的行数。

data _null_;
set prodincl_fr;
do until(rownum=reccount);
  prod=prodcat;
  call execute('%nrstr(%runlimitsquery(&prod))');
  output;
end;
run;

希望你能帮助我。请!

【问题讨论】:

    标签: loops macros sas


    【解决方案1】:

    如果已知的代码生成量少于 64K 字符,您可以使用 SQL 来准备宏调用。我发现这种风格的 codegen 对读者来说特别清楚。

    %macro runlimitsquery(prodcat);
      %put &sysmacroname called with &=prodcat;
    %mend;
    
    data prodcats;
      input prodcat $ @@;
    datalines;
    auto boat home flood aux worker life
    run;
    
    proc sql noprint;
      select 
        cats('%runlimitsquery(',prodcat,')')
      into
        :invoker separated by ' '
      from
        prodcats
      ;
    quit;
    
    %put NOTE: invoker=%superq(invoker); * lets see what is going to be invoked (the codegen);
    
    * invoke the codegen;
    &invoker
    

    日志的最后一部分

    134  %put NOTE: invoker=%superq(invoker); * lets see what is going to be invoked (the codegen);
    NOTE: invoker=%runlimitsquery(auto) %runlimitsquery(boat) %runlimitsquery(home)
    %runlimitsquery(flood) %runlimitsquery(aux) %runlimitsquery(worker) %runlimitsquery(life)
    135
    136  * invoke the codegen;
    137  &invoker
    RUNLIMITSQUERY called with PRODCAT=auto
    RUNLIMITSQUERY called with PRODCAT=boat
    RUNLIMITSQUERY called with PRODCAT=home
    RUNLIMITSQUERY called with PRODCAT=flood
    RUNLIMITSQUERY called with PRODCAT=aux
    RUNLIMITSQUERY called with PRODCAT=worker
    RUNLIMITSQUERY called with PRODCAT=life
    

    如果代码生成将超过 64k 个字符,您可能需要退后一步,重新考虑手头任务的内容和方式。

    【讨论】:

    • 我也会试试这个。谢谢你,理查德!
    【解决方案2】:

    您的代码有一些问题。

    • reccount 不是 prodincl_fr 数据集的变量,因此在该数据步的上下文中不存在。因此,do 语句等同于 do until(rownum=.)
    • 您正在为数据集中的每条记录调用该循环。因此,在该循环的整个迭代过程中,rownum 的值将永远不会改变,并将在当前观察中保持 rownum 的值。您的until 条件永远不会得到满足,您的代码将永远运行。
    • &prod 是对宏变量的引用。但是当您在data 步骤中编写prod=prodcat 时,您创建的是数据步骤变量,而不是宏变量。因此&prod 不存在。
    • 即使如果存在prod 宏变量,它也不会被解析,因为对它的引用是用单引号括起来的。 SAS 不会尝试解析单引号字符串中的宏代码/引用。您需要使用双引号。

    该代码语法正确如下:

    data _null_;
    set prodincl_fr nobs=reccount;
    do i=1 to reccount;
      prod=prodcat;
      call execute('%runlimitsquery('||prod||');');
      output;
    end;
    run;
    

    然而,假设prodincl_fr 包含一千条记录,这会发生什么情况如下:您的数据步将转到prodincl_fr 的第一条记录,启动循环使用 prod 的相同值调用您的宏一千次,这将是当前记录的 prodcat 的值;然后它会移动到第二条记录并使用该记录的 prodcat 值再调用宏一千次;以此类推,直到第 100 条记录。

    因为我确实假设这不是您要查找的内容,并且因为带有 set 语句的数据步骤会隐式迭代所有记录,所以您可以取消 do 循环并像这样编写您的数据步骤:

    data _null_;
    set prodincl_fr;
    call execute('%runlimitsquery('||prodcat||');');
    run;
    

    【讨论】:

    • 我将继续使用 %nrstr() 环绕宏调用,就像在 OP 中一样。堆栈数据步骤完成后发生的所有 %runlimitsquery 宏调用。 ||字符串连接是可以的,但如果数据集变量真的很长,可能会导致一些不稳定的日志记录。考虑使用 CATS() 连接数字和字符数据。最后,宏调用并不总是需要尾随分号。是否取决于宏的作用、调用的上下文、任何副作用文本发射的上下文以及可能的运行时上下文
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多