TryingtoLearn
TryingtoLearn

Reputation: 47

SAS: Running macros inside do loop does not seem to work

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!

Upvotes: 1

Views: 1598

Answers (1)

Joe
Joe

Reputation: 63434

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.

Upvotes: 2

Related Questions