MandyB
MandyB

Reputation: 81

Can I Create a Macro Name from a Macro Variable

I would like to send a set of values into a macro using the INTO function. I do this all the time. However, I would like to the same thing for three different tables and I want to create the INTO macro name with a macro variable that I pass in. Can this be done? It's not working for me. Perhaps I need a preceding command before the macro name to compile it first? Essentially, I want three macro stores to be created: IPCOLS with values from IP_DENOMINATORS, LTCOLS with values from LT_DENOMINATORS, and OTCOLS with values from OT_DENOMINATORS.

Proc Sql noprint;
%macro SUBST() / parmbuff; 
%let i=1;
%let FT=%scan(%bquote(&SYSPBUFF),1);  

%do %while (&FT^=); 
Select DISTINCT STATE into :&FT.COLS separated by ','
from &FT._DENOMINATORS;
      %let i=%eval(&I+1);  
      %let FT=%scan(%bquote(&SYSPBUFF),&I);
      %end;
%mend SUBST;
%SUBST(IP,LT,OT);
Quit;
%Put &&FT.COLS;

Upvotes: 0

Views: 745

Answers (2)

Tom
Tom

Reputation: 51621

The first issue is that your are not trying to reference the macro variable you created. The macro variables will be named IPCOLS, LTCOLS and OPCOLS.

The main issue is that the macro variable(s) might not exist after the macro ends since you created them while the macro was running.

Make sure that the macro variable you are creating is not made local to the macro. When you reference a macro variable while running a macro it first uses the existing macro variable with that name. If none exists then it makes a new one in the local symbol table for the macro. When the macro finishes the local symbol table is gone. You can use the %symexist() function to check if a macro variable already exists or not. If there isn't one you can create a GLOBAL macro variable that will survive past the end of the macro execution.

First thing is don't define the macro in the middle of your other code. Define it first and then use it. That way your code is much easier to read/edit/debug. Depending on how you plan to use it you might even want to have the macro generate the PROC SQL and QUIT statements.

Also there is no need to use PARMBUFF option to pass in a list of values. Just don't use commas between the values. Use space or some other character. (Note that you probably don't want the commas in the values of the macro variables you are creating either, but that depends on what STATE is and how you plan to use those macro variables.)

%macro subst(list);
%local i prefix mvar;
%do i=1 %to %sysfunc(countw(&list,%str( )));
  %let prefix=%scan(&list,&i,%str( ));
  %let mvar=&prefix.cols;
  %if not %symexist(&mvar) %then %global &mvar;
select DISTINCT STATE
  into :&mvar separated by ','
  from &prefix._DENOMINATORS
;
%end;
%mend subst;

proc sql noprint;
%SUBST(IP LT OT);
quit;
%put &=IPCOLS;
%put &=LTCOLS;
%put &=OTCOLS;

Upvotes: 2

Stu Sztukowski
Stu Sztukowski

Reputation: 12909

Try the below formulation instead. A few changes were made:

  1. The %subst() macro is moved outside of PROC SQL so that it can compile first.

  2. The do-while loop is converted into a do i = 1 to n. The do-while loop produced a blank value of &FT in the final iteration.

  3. All macros created are explicitly made global.

Code:

%macro SUBST() / parmbuff; 

    %do i = 1 %to %sysfunc(countw(%bquote(&syspbuff.) ) ); 
        %let ft = %scan(%bquote(&syspbuff.), &i.);
        %global &FT.COLS;

        Select DISTINCT STATE into :&FT.COLS separated by ','
        from &FT._DENOMINATORS;

    %end;
%mend SUBST;

Proc Sql noprint;
    %SUBST(IP,LT,OT);
Quit;

%put IP: &IPCOLS;
%put LT: &LTCOLS;
%put OT: &OTCOLS;

Upvotes: 1

Related Questions