【问题标题】:function style macro in SAS with PROC SQL?SAS中带有PROC SQL的函数样式宏?
【发布时间】:2018-07-13 20:09:37
【问题描述】:

是否可以使这种形式的宏工作?

    %macro tableMath(input1,input2);
    %local result;
    proc sql; ---some code here using inputs--- quit;
    proc sql; ---more code here--- quit;
    proc sql;
    select something into: result
    quit;
    &result
    %mend;

我想对数据集的每次观察运行一些相当复杂的逻辑,并且在我之前使用过的任何其他语言中,这样做的方法是将其封装在一个函数中,该函数每次调用时都会返回一个结果- - 但是,我不确定如何在 SAS 中执行此逻辑。

编辑:input1 和 input2 将是数据集的列,结果将用于在程序的另一部分的其他宏中创建新列。我不需要特定的代码解决方案我只是不明白你应该如何执行传统的函数逻辑,你需要在 SAS 中返回值......

【问题讨论】:

  • stackoverflow.com/questions/33817518/… 在这个问题中,你不能使用没有 % 的任何东西,否则它会污染处理流并且你不会得到正确的返回值。这是否意味着您实际上不能在需要返回值的宏中使用 PROC SQL 或 DATA 步骤??
  • 有一些方法可以从宏中返回一个值。也有编写“函数式宏”的方法,但它们不是真正的函数。宏语言是一种预处理器,因此如果您有调用宏的 PROC SQL 代码,则宏将在 SQL 代码执行之前执行。如果您可以举一个小例子说明您希望如何使用您的功能,那将使这个问题更具体/更容易回答。
  • 您可以尝试使用数据步骤来编码您的复杂逻辑,这将为它迭代的每一行返回一个结果。之后,如果结果符合您希望的标准,请使用call execute() 运行某个步骤。正如昆汀所说,有很多方法......但你必须对我们更具体一点。
  • I want to run some fairly complicated logic on each observation of a dataset 如果您是 SAS 新手,我会说解释一下这个逻辑。通常它并不像看起来那么复杂,或者在 SAS 中可以使用更好的方法,即使只是几个步骤。

标签: sql sas sas-macro


【解决方案1】:

正如 Richard 所写,函数式宏会发出 SAS 代码。开发函数式宏的一般规则是它们只包含宏语言语句。它们包含的任何 SAS 代码都将被发出。从历史上看,这使得编写一个函数式宏来处理数据就像使用 DATA 步一样变得困难/烦人。幸运的是,SAS 添加了一个函数,DOSUBL,它可以更轻松地编写函数式宏,在“侧会话”中执行 SAS 代码并发出结果。见Rick Langston's paper

这是一个函数式宏的示例,它使用 DOSUBL 计算表中的记录数,并发出计数。 (这是一种非常低效的获取记录数的方法,只是在 SQL 中做某事的一个例子)。

%macro SQLcount(table);
  %local rc emit; 

  %let rc=%sysfunc(dosubl(%nrstr(
     proc sql noprint;
      select count(*) into :emit trimmed
      from &table
     quit;
  )));

  &emit 
%mend ;

可以这样使用:

proc sql ;
  select name
        ,%SQLcount(sashelp.shoes) as ShoeCount  /*emits 395*/
  from sashelp.class
  ;
quit ;

上述步骤运行时,会从 sashelp.class 返回 19 行名称,每行 ShoeCount 的值为 395。请注意,宏 SQLcount 只执行一次。在编译/解释 PROC SQL 步骤时,可以看到对 SQLcount 的调用,执行宏并发出 395。步骤变为:

proc sql ;
  select name
        ,395 as ShoeCount  /*emits 395*/
  from sashelp.class
  ;
quit ;

DOSUBL 使用“侧会话”来执行代码,它允许您在主会话解释 PROC SQL 步骤时在侧会话中执行 PROC SQL 步骤。

我无法从您的问题中看出这种用例是否是您想要的。您可能需要一个函数样式的宏,您可以在其中将值从表中传递给它,并让宏在每个值上执行并返回一些内容。假设你有一个表,它是一个表名列表,并且想使用 SQL 来获取每个表中的记录数:

data mytables ;
  input table $20. ;
  cards ;
sashelp.shoes
sashelp.class
sashelp.prdsale
;
quit ;

您可以通过使用 resolve() 函数从数据构建宏调用,延迟宏的执行直到 SELECT 语句执行:

proc sql ;
  select table
        ,resolve('%SQLcount('||table||')') as count
  from mytables
  ;
quit ;

这样,SQLcount 将被调用 3 次,并返回每个数据集中的记录数。

table                 count
---------------------------
sashelp.shoes         395
sashelp.class         19
sashelp.prdsale       1440

解释 PROC SQL 步骤时看不到宏调用,因为它被单引号隐藏。然后,当 SELECT 语句执行时,resolve 函数调用宏,将 table 的值作为参数值传递,宏发出记录计数。这类似于使用数据驱动宏调用的 CALL EXECUTE 方法。

【讨论】:

  • 谢谢,帮了大忙!但是,我仍然在实施它时遇到了一些麻烦。我希望将表中的变量传递给宏,并让它为每个观察结果返回一个值。如上所述,我使用 DOSUBL 来形成我的宏,但是当我使用 resolve('%macroName('||tableVariable||')') 语法时,我收到错误“连接需要字符操作数”。顺便说一句,当不使用解析提供一个值时,当我不引用 tableVariable 时,它​​会为每个观察结果运行,但在宏中搜索“tableVariable”而不是 SQL 变量的值
  • 听起来你有一个数字变量或值,你正试图与 || 连接。运算符,它不起作用。如果是这样,您可以尝试切换到 CATS 函数进行连接,或者将数值变量转换为字符。 CATS 看起来像:resolve(cats('%SQLcount(',table,')')) as count
【解决方案2】:

你说你想:

对数据集的每次观察运行一些相当复杂的逻辑

为此,您应该使用 SAS 语言而不是宏处理器或 PROC SQL。您可以使用数据步骤。或者对于更复杂的逻辑,您应该查看 PROC DS2。

【讨论】:

    【解决方案3】:

    听起来您可能想使用proc fcmp 创建一个 FCMP 函数。这基本上是一种创建您自己的 SAS 函数的方法,这些函数可以在 proc sqldata 步骤中使用。例如:

    /******************************************************************************
    ** PROGRAM:  COMMON.FCMP_DIV.SAS
    **
    ** DESCRIPTION: PERFORMS A MATHEMATICAL DIVISION BUT WILL RETURN NULL IF THE
    **              NUMERATOR OR DENOMINATOR IS MISSING (OR IF THE DIVISOR IS 0).
    **
    ******************************************************************************/
    
    proc fcmp outlib=common.funcs.funcs;
    
      function div(numerator, denominator);
    
        if numerator eq . or denominator in (0,.) then do;
          return(.);
        end;
        else do;
          return(numerator / denominator);
        end;
    
      endsub;
    run;
    

    示例用法(示例是数据步骤,但在 SQL 中同样适用):

    data x;
      x1  = div(1,0);
      x2  = div(1,.);
      x3  = div(1,1);
    
      x4  = div(0,0);
      x5  = div(0,.);
      x6  = div(0,1);
    
      x7  = div(.,0);
      x8  = div(.,.);
      x9  = div(.,1);
    
      put _all_;
    run;
    

    【讨论】:

    • 我阅读 OP 时希望创建一个函数,该函数本身将运行 PROC SQL 步骤并返回一个值。并且该函数可能用于 SQL 选择语句或任何地方。我假设使用 FCMP(和 DS2),您无法在另一个 SQL 步骤“内”运行 PROC SQL 步骤,对吗?
    • @Quentin 同意,但我认为在不了解他们到底想要做什么的情况下,FCMP 可能有助于“重新定义”要求。想到提及它可能会为 OP 开辟替代途径。
    【解决方案4】:

    宏函数不返回值。宏函数可以“发出”源代码

    • 是一个或多个步骤,
    • 这是一个sn-p,代码被合并到一个语句中,
    • 这是一个或多个作为步骤一部分的语句,

    对于您想要在 SQL 中“做”事情的情况,您可以编写 SQL 视图,然后

    • %sysfunc(open())
    • 打开
    • 处理过
      • %sysfunc(set())
      • %sysfunc(getvarn())
      • %sysfunc(getvarc())

    并非所有 SQL 功能都可以通过这种技术使用 - select something into :result 必须是带有 select something 的视图,而宏将 getvarc 读取结果。

    以 open/set/get 方式完成的访问不会导致出现 step 边界,因此宏处理可以继续其逻辑并最终发出源代码供 sn-p 级别使用。 (消费者是处理宏代码的SAS执行者,隐式编译运行SAS步骤)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多