Hi all,
I have the following code:
%let performance_range_start = 2019-11-01;
%let performance_range_end = 2020-02-01;
%macro inner2;
%put 'UPDATE DATA FOR:' &person_id;
%mend;
%macro inner1;
data _NULL_;
set pg.people(obs=5);
CALL SYMPUT("person_id",strip(person_id));
put 'START WORK:' person_id;
%inner2;
run;
%mend;
%macro outer;
%let start=%sysfunc(inputn(&performance_range_start,yymmdd10.));
%let end=%sysfunc(inputn(&performance_range_end,yymmdd10.));
%let dif=%sysfunc(intck(month,&start,&end));
%do i=0 %to &dif;
%let r_start=%sysfunc(intnx(month,&start,&i,b),date9.);
%put 'PROCESS:' &r_start;
%inner1;
%end;
%mend;
%outer;
which outputs:
START WORK:1
START WORK:2
START WORK:3
START WORK:4
START WORK:5
'PROCESS:' 01DEC2019
'UPDATE DATA FOR:' 5
...
...
I was expecting:
START WORK:1
'PROCESS:' 01DEC2019
'UPDATE DATA FOR:' 1
START WORK:2
'PROCESS:' 01DEC2019
'UPDATE DATA FOR:' 2
START WORK:3
'PROCESS:' 01DEC2019
'UPDATE DATA FOR:' 3
START WORK:4
'PROCESS:' 01DEC2019
'UPDATE DATA FOR:' 4
...
...
Any pointers to where I am going wrong here? Do I somehow need to wait for the inner macros to finish before processing the next observation on the outer macro?
Many thanks in advance,
Olli
Timing.
When your have something like that calls a macro inside a data step:
data _NULL_; set pg.people(obs=5); CALL SYMPUT("person_id",strip(person_id)); put 'START WORK:' person_id; %inner2; run;
The macro %inner2 executes when the data _null_ step compiles.
So you may want:
data _NULL_; set pg.people(obs=5); CALL SYMPUT("person_id",strip(person_id)); put 'START WORK:' person_id; run; %inner2;
What do you expect from this? You are reading 5 observations and the value of the macro variable &person_id will be that of the last of the 5 (or any number) read.
If you want to do something for each record in your input set PG.People you may be looking for the function CALL EXECUTE to create the code that runs after the data step completes.
Timing.
When your have something like that calls a macro inside a data step:
data _NULL_; set pg.people(obs=5); CALL SYMPUT("person_id",strip(person_id)); put 'START WORK:' person_id; %inner2; run;
The macro %inner2 executes when the data _null_ step compiles.
So you may want:
data _NULL_; set pg.people(obs=5); CALL SYMPUT("person_id",strip(person_id)); put 'START WORK:' person_id; run; %inner2;
What do you expect from this? You are reading 5 observations and the value of the macro variable &person_id will be that of the last of the 5 (or any number) read.
If you want to do something for each record in your input set PG.People you may be looking for the function CALL EXECUTE to create the code that runs after the data step completes.
There is no reason for %inner2 to use any macro language. You could easily change its definition to be:
%macro inner2;
put 'UPDATE DATA FOR:' person_id;
%mend;
In fact, you might just include the PUT statement in the DATA step itself, without definining %inner2 at all. You created the START WORK logic with no macro language, you could be able to similarly create the UPDATE DATA FOR logic with no macro language.
With a little more of a description of the intention, you might be able to get rid of all the macro language except for the two %LET statements at top. The DATA step "knows" how to manipulate data much more easily than macro language does.
Try this non-macro approach
%let start = '01Nov2019'd;
%let end = '01Feb2020'd;
data _null_;
set pg.people (obs=5);
format r_start date9.;
dif=intck('month',&start,&end);
do i=0 to dif;
put 'START WORK: ' person_id;
r_start=intnx('month',&start,i,'b');
put 'PROCESS: ' r_start;
put 'UPDATE DATA FOR: ' person_id;
put;
end;
run;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.