I need to run a series of macros multiple times. To do so, I've built a new macro that has inside of it a do-loop, which is supposed to run "i" number of times, and with each iteration of the do-loop, the series of macros referenced above are supposed to run.
Here is the essence of code (note that the first proc sql essentially takes from a dataset called "DatesRange" and placed a range of dates into the variable "varlist_Dates"; the range of dates within this variable is used for Macro1, Macro2, Macro3).
%macro MultipleTimes;
proc sql noprint;
select distinct Date
into :varlist_Dates separated by ' '
from DatesRange
order by Date;
quit;
%do i = 1 %to 5;
%let date = %scan(&varlist_Dates.,&i.);
%Macro1;
%Macro2;
%Macro3;
%end;
%mend;
I'm finding that it stops at i=1 and never proceeds. I'm completely unclear on why. I've experimented by playing around with removing some macros and keeping others but nothing seems to work. I feel like there may be something more fundamental about my methodology that is off, because there's something I don't know about SAS and about the way a macro inside of a do-loop inside of a macro works.
Any help would be much appreciated. Thanks!
First: check if i
is used in any of those macros. I bet it is. It's probably being changed to something higher than 5 (thus exiting after one loop).
In SAS, when you reference a macro variable that already exists, it is defined as having the scope of the most local symbol table that contains it, unless you use %local
to specify it as local to the current macro. So if you throw %let i=1 to 5;
around in multiple nested macros, it will be using the same &i.
As an example, see the following code:
%let i=A;
%macro outer;
%put &=i;
%do i=1 %to 5;
%put OUTER FIRST: &=i;
%inner;
%put OUTER LAST: &=i;
%end;
%mend outer;
%macro inner;
%do i=1 %to 5;
%put INNER: &=i.;
%end;
%mend;
%put GLOBAL FIRST: &=i;
%outer;
%put GLOBAL LAST:&=i;
Notice how &i
is always the same value. That's not what you mean, now is it?
Now, on the other hand, &i
does get a different value in each bit if you do it right:
%let i=A;
%macro outer;
%local i;
%put &=i;
%do i=1 %to 5;
%put OUTER FIRST: &=i;
%inner;
%put OUTER LAST: &=i;
%end;
%mend outer;
%macro inner;
%local i;
%do i=1 %to 5;
%put INNER: &=i.;
%end;
%mend;
%put GLOBAL FIRST: &=i;
%outer;
%put GLOBAL LAST:&=i;
And if you %put _all_
in INNER you will see this in action.
Second: Don't write it this way. You're going to so much effort, just write it a better way to begin with.
%Macro RunMyMacros(date=);
%Macro1; *I hope you use &date as a parameter in these and not as a global;
%Macro2;
%Macro3;
%mend RunMyMacros;
proc sql noprint;
select distinct cats('%RunMyMacros(date=',Date,')')
into :calllist_Dates separated by ' '
from DatesRange
order by Date;
quit;
&calllist;
That's far easier to use, troubleshoot, and run, and doesn't require hardcoding the number of valid dates or anything else in it.