Hi,
please check out this code snippet which is driving me crazy:
data dataset;
par = 1;
var = 'Valore';
run;
%macro mmm(par1);
%put macrovar1=¯ovar;
proc sql;
select var || put(time(),time10.) into :macrovar from dataset where par = &par1;
quit;
%put macrovar2=¯ovar;
%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
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.
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.
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.
Thank you very very much, I understand, I'll change the code as you suggest.
So two last questions:
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 ¯ovar.
Could you be more specific?
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=¯ovar;
%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 ¯ovar 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
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.
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.
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
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 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.