用于从数据变量生成宏变量的宏函数

时间:2014-12-17 17:01:35

标签: sas sas-macro

data sample;
    input x $;
    datalines;
one
two
three
;

%macro variable_to_macvar(variable=, dataset=);
    proc sql noprint;
        select &variable into : outlist separated by ' ' 
        from &dataset;
    quit;
&outlist
%mend variable_to_macvar;

%put %variable_to_macvar(variable=x, dataset=sample);

预期输出:one two three相反,我收到了错误。为什么?这可以解决吗?

我已经成功创建了一个非常相似的形式的其他宏,其中函数"返回"使用宏末尾的& macro变量而不使用分号的值。例如,这是一种类似的功能:

%macro zcat(first=5, last=15, prefix=freq); 
    %let x=;   
    %do i = &first %to &last;
        %let x=&x &prefix.&i;
    %end;
    &x
%mend zcat;
%put %zcat();

4 个答案:

答案 0 :(得分:3)

您不能以您尝试在此处执行的方式执行涉及运行proc或数据步骤的宏。您需要使用类似%sysfunc(dosubl(proc sql...))的内容才能使其正常工作(假设您有SAS 9.3+ - 请参阅上面的Joe的回答)。否则,您不能在函数式宏中使用proc sql。

有关dosubl的更多详细信息: http://support.sas.com/documentation/cdl/en/lefunctionsref/67398/HTML/default/viewer.htm#p09dcftd1xxg1kn1brnjyc0q93yk.htm

这有点繁琐,但如果您真的想在早期版本的SAS中将其作为函数式宏工作,则可以使用openfetchobs和{getvarc来构造它。 {1}}而是功能。

更新:这是一个示例(使用call set而不是getvarc,因为事实证明这更简单),以防任何人需要在SAS 9.2或更早版本中执行此操作。

%macro variable_to_macvar(var,ds);
    %local rc dsid i;
    %let &var =;
    %global outlist;
    %let outlist=;
    %let dsid = %sysfunc(open(&ds,i));
    %syscall set(dsid);
    %let rc = 0;
    %let i = 0;
    %do %while(&rc = 0);
        %let i = %eval(&i + 1);
        %let rc = %sysfunc(fetchobs(&dsid,&i));
        %if &rc = 0 %then %let outlist = &outlist &&&var;
    %end;
    %let rc = %sysfunc(close(&dsid));
    &outlist
%mend;

%put %variable_to_macvar(var=x, ds=sample);

现在适用于视图和普通数据集。

答案 1 :(得分:2)

DOSUBL在9.3中是可用的(但是实验性的)(至少,9.3TS1M2,我有)。这就是你如何做到的。

data sample;
    input x $;
    datalines;
one
two
three
;

%macro variable_to_macvar(variable=, dataset=);
   %let rc=%sysfunc(dosubl(%str(
    proc sql noprint;
        select &variable into : outlist separated by ' ' 
        from &dataset;
    quit;
    )));
&outlist
%mend variable_to_macvar;

%put %variable_to_macvar(variable=x, dataset=sample);;

如果您不能使用DOSUBL,或者想要避免使用实验性内容,则可以使用PROC FCMP而不是宏来执行此操作。如果您想编写函数,PROC FCMP可能适合您:实际上能够编写函数,而不必处理宏语言的烦恼。

答案 2 :(得分:1)

最后改变你的代码

%global outlist;
%variable_to_macvar(variable=x, dataset=sample);
%put &outlist;

%put想要只解析一个宏变量或一个值。它无法调用程序。所以调用你的宏,然后打印结果。

此外,从宏定义中删除&outlist。对不起,我最初错过了。

编辑:替代。

将宏定义更改为

%macro variable_to_macvar(variable=, dataset=);
    proc sql noprint;
        select &variable into : outlist separated by ' ' 
        from &dataset;
    quit;
%put &outlist
%mend variable_to_macvar;

只需将%放入宏内。

%variable_to_macvar(variable=x, dataset=sample);

会将字符串打印到日志中。

答案 3 :(得分:0)

我们有一个实用程序宏,它可能是我们最常用的代码之一。它类似于@ user667489提供的代码,但包含一些不错的功能,包括错误捕获,允许字符和数字变量,允许您指定分隔符,引号,引号字符,数据集的过滤器等等。

我们只是将这个宏放在我们的自动调用库中,以便它可以用于我们所有的程序。运行宏的一些示例:


示例1 - 默认行为:

%put %variable_to_macvar(var=x, ds=samplex);

结果1:

one,two,three

不完全是所需的输出,因为默认的分隔符是逗号,虽然可以轻松更改...


示例2 - 指定使用空格字符作为分隔符:

%put %ds2list(iDs=samplex, iField=x, iDelimiter=%str( ));

结果2:

one two three

示例3 - 引用&示例用法

data names;
  input name $;
  datalines;
John
Jim
Frankie
;
run;

%put %ds2list(iDs=names, iField=name, iQuote=1);

proc sql noprint;
  create table xx as 
  select *
  from sashelp.class
  where name in (%ds2list(iDs=names, iField=name, iQuote=1))
  ;
quit;

结果3:

以下内容将打印到日志中:

'John','Jim','Frankie'

注意我们不需要将结果保存到宏变量中以在SQL语句中使用它! Swweeet!这适用于SQL直通查询以及您可以将其抛出的任何其他数据步骤或proc语句。在上面的示例中,单行返回为' John'是找到的唯一一场比赛......


无论如何,这就是我们的解决方案......使用它的时间大约为10年,对我来说效果很好。这是宏:

/***************************************************************************
**  PROGRAM: MACRO.DS2LIST.SAS
**
**  UTILITY PROGRAM THAT DETECTS RETURNS A LIST OF FIELD VALUES FROM A 
**  DATASET IN DELIMITED FORMAT.
**
**  PARAMETERS:
**  iDs       : THE LIBNAME.DATASET NAME THAT YOU WANT TO CHECK.
**  iField    : THE FIELD THAT CONTAINS THE VALUES YOU WANT RETURNED IN A 
**              DELIMITED FORMAT.
**  iDelimiter: DEFAULT IS A COMMA. THE DELIMITER TO USE FOR THE RETURNED LIST.
**  iDsOptions: ANY STANDARD DATASET OPTIONS THAT YOU WOULD LIKE TO APPLY SUCH 
**              AS A WHERE STATEMENT.
**  iQuote    : (0=NO,1=YES). DEFAULT=0/NO. DETERMINES WHETHER THE RETURNED 
**              LIST IS QUOTED OR NOT.
**  iQuoteChar: (SINGLE,DOUBLE) DEFAULT=SINGLE. SPECIFIES WHETHER SINGLE
**              OR DOUBLE QUOTES ARE USED WHEN QUOTING THE RETURNED LIST
**
*****************************************************************************
** VERSION:
**  1.8 MODIFIED: 11-OCT-2010  BY: KN
**      ALLOW BLANK CHARACTER VALUES AND ALSO REMOVED TRAILING
**      ALLOW PARENTHESES IN CHARACTER VALUES
*****************************************************************************/

%macro ds2list(iDs=, iField=, iDsOptions=, iDelimiter=%str(,), iQuote=0, iQuoteChar=single);
  %local dsid pos rc result cnt quotechar;

  %let result=;
  %let cnt=0;

  %if &iQuote %then %do;
    %if "%upcase(&iQuoteChar)" eq "DOUBLE" %then %do;
      %let quotechar = %nrstr(%");
    %end;
    %else %if "%upcase(&iQuoteChar)" eq "SINGLE" %then %do;
      %let quotechar = %nrstr(%');
    %end;
    %else %do;
      %let quotechar = %nrstr(%");
      %put WARNING: MACRO.DS2LIST.SAS: PARAMETER IQUOTECHAR INCORRECT. DEFAULTED TO DOUBLE;
    %end;
  %end;
  %else %do;
    %let quotechar = ;
  %end;

  /*
  ** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
  */
  %if "&iDs" ne "" and "&iField" ne "" %then %do;

    %let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
    %if &dsid %then %do;

      %let pos=%sysfunc(varnum(&dsid,&iField));
      %if &pos %then %do;

        %let rc=%sysfunc(fetch(&dsid));
        %do %while (&rc eq 0);

          %if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
            %let value = %qsysfunc(getvarc(&dsid,&pos));
            %if "%trim(&value)" ne "" %then %do;
              %let value = %qsysfunc(cats(%nrstr(&value)));
            %end;
          %end;
          %else %do;
            %let value = %sysfunc(getvarn(&dsid,&pos));
          %end;

          /* WHITESPACE/CARRIAGE RETURNS REMOVED IN THE BELOW LINE */
          /* TO ENSURE NO WHITESPACE IS RETURNED IN THE OUTPUT.    */
          %if &cnt ne 0 %then %do;%unquote(&iDelimiter)%end;%unquote(&quotechar&value&quotechar.)

          %let cnt = %eval(&cnt + 1);
          %let rc  = %sysfunc(fetch(&dsid));
        %end;

        %if &rc ne -1 %then %do;
          %put WARNING: MACRO.DS2LIST.SAS: %sysfunc(sysmsg());
        %end;

      %end;
      %else %do;
        %put ERROR: MACRO.DS2LIST.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
      %end;
    %end;
    %else %do;
      %put ERROR: MACRO.DS2LIST.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
    %end;

    %let rc=%sysfunc(close(&dsid));

  %end;
  %else %do;
    %put ERROR: MACRO.DS2LIST.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
  %end;

%mend;