BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Edoedoedo
Pyrite | Level 9

Hi,

please check out this code snippet which is driving me crazy:

data dataset;

  par = 1;

  var = 'Valore';

run;


%macro mmm(par1);

  %put macrovar1=&macrovar;


  proc sql;

  select var || put(time(),time10.) into :macrovar from dataset where par = &par1;

  quit;


  %put macrovar2=&macrovar;

%mend;


data _NULL_;

  call execute("%mmm(1)");

run;

I expect that macrovar1 puts "not resolved" and macrovar2 puts e.g. "Valore 15:00:00". Instead, I get "not resolved" either in macrovar1 and in macrovar2. Moreover, "macrovar" becomes oddly a global macro variable, hence if I execute again this snippet I get "Valore 15:00:00" either in macrovar1 and in macrovar2, which does not make any sense since I expect macrovar1 to be "not resolved" and macrovar2 to be "Valore 15:01:00" i.e. a new value instead of keeping the old value.

However, if I replace the last data step simply with %mmm(1); it works exactly as expected. So what is "call execute" doing wrong?

I hope I made myself clear.

Thanks a lot

Regards

1 ACCEPTED SOLUTION

Accepted Solutions
Kurt_Bremser
Super User

The macro is executed when it is called during the compilation phase of the data _NULL_ step, but the proc sql runs after the text created by the macro is handed over to the SAS system when the data _NULL_ step has finished its execution phase. Therefore the macro reaches its %mend long before the sql ever runs.

You can watch this when you put

options mlogic mprint;

right before the data _null_ step.

View solution in original post

9 REPLIES 9
Kurt_Bremser
Super User

The macro is executed when it is called during the compilation phase of the data _NULL_ step, but the proc sql runs after the text created by the macro is handed over to the SAS system when the data _NULL_ step has finished its execution phase. Therefore the macro reaches its %mend long before the sql ever runs.

You can watch this when you put

options mlogic mprint;

right before the data _null_ step.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

To be honest, I don't see why you would want to be selecting data into a macro variable in code which is called by call execute.  The basic premise of using call execute is to generate code based on the output from a datastep.  So if i take a problem, say I want to print all 4 ABC variables, then I wouldn't call execute a macro which creates a list of of these variables, as I already have the datastep for such a thing:

data _null_;

     do i="ABC1","ABC2","ABC3","ABC4";

          call execute('%print_macro (var='||strip(i)||');');

     end;

run;

as KurtBremser has stated, you are trying to generate code (via the pre-processor) at two separate stages which doesn't make sense.  Get the information you would put in macro variables into a dataset - remembering that you can do pretty much anything here, distinct lists for loops, do, calculations etc. then use that input dataset as the driver for call execute to generate the code from.

Edoedoedo
Pyrite | Level 9

Thank you very very much, I understand, I'll change the code as you suggest.

So two last questions:

  • why that code made macrovar global? just curious
  • is there a function to resolve a macrovariable at execution-time without using neither a datastep nor a proc?
Kurt_Bremser
Super User

Since the SQL runs after the data _null_ step and therefore outside of the macro, it puts macrovar in the global table.

I don't quite understand your second question. Macro variables are resolved when referenced with &macrovar.

Could you be more specific?

Edoedoedo
Pyrite | Level 9

Thanks, I mean that for instance the macro would be:

%macro mmm(par1);

 

  proc sql;

  select var || put(time(),time10.) into :macrovar from dataset where par = &par1;

  quit;

     options something=&macrovar;

%mend;


meaning that for instance I need to use the macrovar variable (calculated within the macro) later in the same macro neither in a datastep nor in a proc step (in the example I'm using "options" which does not have any sense, it is just to explain what I mean). As you taught me, this code would not work because &macrovar is resolved at compile time (hence undefined).

How would you rewrite that line in order to use that macrovariable calculated just before? I'm looking for something like "options something=symget('macrovar');" (which is awful, I know).


Thanks again

RW9
Diamond | Level 26 RW9
Diamond | Level 26

You wouldn't do it that way.  So say I want to do that bit of code:

%let par1=something;

data _null_;

     set dataset (where=(par=&par1.)); 

     call execute(cats('options something=',put(time(),time10.),';'));

run;


Note that the above will generate the options statement once for each record returned from dataset where the where condition is true. 

Kurt_Bremser
Super User

If you want a "persistent" macro variable that keeps its value from macro call to macro call, you need to put it into the global macrovariable table by using the %global macrovar; statement.

Your initial problem was that, by using call execute within a data step, you created confusion about when what piece of code was executed. I advise to follow the KISS principle and not call a macro with call execute, but a "macro-less" version of the code with call execute, unless you completely know what you're doing (which will take some time regarding the use of the macro language).

I was alos initially baffled by your problem.

Ron_MacroMaven
Lapis Lazuli | Level 10

your question does not make sense

> is there a function to resolve a macrovariable at execution-time without using neither a datastep nor a proc?

we have macro variables (%let)

and macro definitions (%macro x ... %mend x;)

resolution means

1. substitution:

1.1 assignment %let a = apples;

1.2 substitution: %put I like &a;

also: %put info: &=a;

2. expansion:

2.1 definition: %macro do_loop(dim=3)%do i = 1 %to &dim; %put echo &=i;%end;%mend;

2.2 expand: %do_loop(dim=4)

%put i=1;

%put i=2;

%put i=3;

%put i=4;

summary:

macro variable values are referenced, substituted, with an ampersand before the macro variable name

macro definitions are expanded, with a percent sign before the macro definition name

please reframe your question

Tom
Super User Tom
Super User

Consider the timing of when the macro runs.

In your original line:

  call execute("%mmm(1)");

You are expanding the macro BEFORE the data step executes and storing the code generated by that macro into the literal string that you pass to call execute.

So the %PUT statements run BEFORE the data step starts and what is passed to CALL EXECUTE are the lines of code that the macro generated.  It is as if you coded

  call execute('proc sql; select ....');


If you change to using single quotes:

  call execute('%mmm(1)');

then the macro is expanded when it is pushed on the CALL EXECUTE stack.  So the %PUTs run while the code is being put onto the stack and the same SQL code is put on the stack to run after the data step finishes.


If you change to using %NRSTR() inside of single quotes:

  call execute('%nrstr(%%mmm)(1)');

then the macro call is pushed onto the CALL EXECUTE stack.  So now the %PUTs run after the data step has finished.


SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 9 replies
  • 5298 views
  • 10 likes
  • 5 in conversation