cycled macro variable needs to be run twice to produce desired outcome

Accepted Solution Solved
Reply
Contributor
Posts: 49
Accepted Solution

cycled macro variable needs to be run twice to produce desired outcome

Hello all,

 

I am trying to amend the code that is attached so that I would not have to run it twice to get the desired outcome. When I run it once I get this warning:

WARNING: Apparent symbolic reference A not resolved.

WARNING: Apparent symbolic reference B not resolved.

WARNING: Apparent symbolic reference MONTH_END_DT not resolved.

WARNING: Apparent symbolic reference YM not resolved.

&A &B &month_end_dt &YM

WARNING: Apparent symbolic reference A not resolved.

WARNING: Apparent symbolic reference B not resolved.

WARNING: Apparent symbolic reference MONTH_END_DT not resolved.

WARNING: Apparent symbolic reference YM not resolved.

 

However, after rerunning the code it works fine - any idea how to restructure the code to provide the desired outcome within the first run? Thanks

%macro d(country,cycle);

data _null_;
A = put(intnx('month',mdy(substr(&cycle,1,length(&cycle)-2),1,substr(&cycle,length(&cycle)-1,2)),-1,'end'),date9.)||':23:59:59.000';
CALL SYMPUT('A',A);
B = INPUT(A,DATETIME22.3);
CALL SYMPUT('B',B);
month_end_dt = INTNX('DTMONTH',B,-1,'E');
call symput ('month_end_dt',put(month_end_dt,datetime22.3));
year_month = put(year(datepart(month_end_dt)),4.)||"_"||put(month(datepart(month_end_dt)),Z2.);
call symput ('ym',year_month);
run;

%PUT &A &B &month_end_dt &YM;

%mend d;

data _null_;
   array x[2] $2 ('UK' 'DE');
   	do j=1 to dim(x);
		do i = 1 to 1;
			country = x[j];
			cycle = compress(strip(trim(cat(i,'17'))));
			call execute ('%d('!!trim(country)!!','!!trim(cycle)!!')');
		end;
  	end;
run;

Accepted Solutions
Solution
‎03-02-2017 07:30 AM
Super User
Posts: 7,465

Re: cycled macro variable needs to be run twice to produce desired outcome

When you call execute a macro, the macro call itself is put into the SAS processing queue, the macro processor kicks in and executes the macro, but all code created by the macro will only be executed once the current data step has finished.

When you activate

options mlgoc mprint;

before the data step that call executes the macro, you'll see that all macro logic runs before the data step ends, and that only then will the data steps created by the macro calls start.

Since these data steps now run outside(!) a macro, they put their macro variables in the global table, so they are available after that.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers

View solution in original post


All Replies
Solution
‎03-02-2017 07:30 AM
Super User
Posts: 7,465

Re: cycled macro variable needs to be run twice to produce desired outcome

When you call execute a macro, the macro call itself is put into the SAS processing queue, the macro processor kicks in and executes the macro, but all code created by the macro will only be executed once the current data step has finished.

When you activate

options mlgoc mprint;

before the data step that call executes the macro, you'll see that all macro logic runs before the data step ends, and that only then will the data steps created by the macro calls start.

Since these data steps now run outside(!) a macro, they put their macro variables in the global table, so they are available after that.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Contributor
Posts: 49

Re: cycled macro variable needs to be run twice to produce desired outcome

Great, thanks Kurt!

Contributor
Posts: 49

Re: cycled macro variable needs to be run twice to produce desired outcome

Kurt I amended the code as you mentioned (attached) but it still gives the warning message?

%macro d(country,cycle);

data _null_;
A = put(intnx('month',mdy(substr(&cycle,1,length(&cycle)-2),1,substr(&cycle,length(&cycle)-1,2)),-1,'end'),date9.)||':23:59:59.000';
CALL SYMPUT('A',A);
B = INPUT(A,DATETIME22.3);
CALL SYMPUT('B',B);
month_end_dt = INTNX('DTMONTH',B,-1,'E');
call symput ('month_end_dt',put(month_end_dt,datetime22.3));
year_month = put(year(datepart(month_end_dt)),4.)||"_"||put(month(datepart(month_end_dt)),Z2.);
call symput ('ym',year_month);
run;

%PUT &A &B &month_end_dt &YM;

%mend d;

options mlogic mprint;

data _null_;
   array x[2] $2 ('UK' 'DE');
   	do j=1 to dim(x);
		do i = 1 to 1;
			country = x[j];
			cycle = compress(strip(trim(cat(i,'17'))));
			call execute ('%d('!!trim(country)!!','!!trim(cycle)!!')');
		end;
  	end;
run;
Super User
Posts: 7,465

Re: cycled macro variable needs to be run twice to produce desired outcome

Eliminate the macro that doesn't do anything (besides putting values to the log):

data _null_;
array x[2] $2 ('UK' 'DE');
do j=1 to dim(x);
  do i = 1 to 1;
    country = x[j];
    cycle = compress(strip(trim(cat(i,'17'))));
    A = put(intnx('month',mdy(substr(cycle,1,length(cycle)-2),1,substr(cycle,length(cycle)-1,2)),-1,'end'),date9.)||':23:59:59.000';
    B = INPUT(A,DATETIME22.3);
    month_end_dt = INTNX('DTMONTH',B,-1,'E');
    year_month = put(year(datepart(month_end_dt)),4.)||"_"||put(month(datepart(month_end_dt)),Z2.);
    put a= b= month_end_dt= year_month=;
  end;
end;
run;
---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Contributor
Posts: 49

Re: cycled macro variable needs to be run twice to produce desired outcome

The problem is that within the macro there are many sql procedures and datasteps that I did not mention. I just want to loop through the combinations of countries and cycles while there are other macro variables derived from cycle. Thanks

Super User
Posts: 7,465

Re: cycled macro variable needs to be run twice to produce desired outcome

Then I'd take all macro logic out of the macro, so it becomes purely a wrapper for the repeating code, and calculate all macro values in the data _null_ step. Use all parameters calculated in the data step as macro parameters.

Make your code as simple as possible, debugging what you had initially is a major PITA (as you have already noticed, or you wouldn't be here)

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Super User
Posts: 7,465

Re: cycled macro variable needs to be run twice to produce desired outcome

PS options mlogic mprint is just there to show you what happens. It's not a solution, as you have not stated what issue you need to solve with the code.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Super User
Posts: 5,372

Re: cycled macro variable needs to be run twice to produce desired outcome

Except for the messages, the results are probably accurate.  CALL EXECUTE runs macro statements such as %PUT right away, before running the DATA step that assigns values to the macro variables.  The solution is to delay running the macro statements by applying %NRSTR:

 

call execute ('%d(' || trim(country) || ',' || trim(cycle) || ')' );

 

becomes 

 

call execute (%nrstr('%d(' || trim(country) || ',' || trim(cycle) || ')') );

Contributor
Posts: 49

Re: cycled macro variable needs to be run twice to produce desired outcome

Hi, I tried what you suggested and received same warning message:

WARNING: Apparent symbolic reference A not resolved.

WARNING: Apparent symbolic reference B not resolved.

WARNING: Apparent symbolic reference MONTH_END_DT not resolved.

WARNING: Apparent symbolic reference YM not resolved.

&A &B &month_end_dt &YM

WARNING: Apparent symbolic reference A not resolved.

WARNING: Apparent symbolic reference B not resolved.

WARNING: Apparent symbolic reference MONTH_END_DT not resolved.


%macro d(country,cycle); data _null_; A = put(intnx('month',mdy(substr(&cycle,1,length(&cycle)-2),1,substr(&cycle,length(&cycle)-1,2)),-1,'end'),date9.)||':23:59:59.000'; CALL SYMPUT('A',A); B = INPUT(A,DATETIME22.3); CALL SYMPUT('B',B); month_end_dt = INTNX('DTMONTH',B,-1,'E'); call symput ('month_end_dt',put(month_end_dt,datetime22.3)); year_month = put(year(datepart(month_end_dt)),4.)||"_"||put(month(datepart(month_end_dt)),Z2.); call symput ('ym',year_month); run; %PUT &A &B &month_end_dt &YM; %mend d; /*options mlogic mprint;*/ data _null_; array x[2] $2 ('UK' 'DE'); do j=1 to dim(x); do i = 1 to 1; country = x[j]; cycle = compress(strip(trim(cat(i,'17')))); call execute (%nrstr('%d(' || trim(country) || ',' || trim(cycle) || ')') ); end; end; run;
Super User
Posts: 5,372

Re: cycled macro variable needs to be run twice to produce desired outcome

It's possible the quotes are in the wrong place:

 

call execute (%nrstr('%d(' || trim(country) || ',' || trim(cycle) || ')') );

 

Try it this way:

call execute ('%nrstr(%d(' || trim(country) || ',' || trim(cycle) || '))' );

Contributor
Posts: 49

Re: cycled macro variable needs to be run twice to produce desired outcome

Great, this is exactly what I needed. Thanks for your help.

Super User
Super User
Posts: 7,727

Re: cycled macro variable needs to be run twice to produce desired outcome

So what is it your actually trying to do as this looks like a bit of a pickle.  You say you want to do code for some by groups (country, cycle) - so my first question is going to be, why do you not put country and cycle into the data, and then do by group processing on it.  This would simplify all your coding.

The second thing is that you seem to be creating macro variables for dates, times and such like, probably to use later in the macro.  This again is not a good way to work with "data" as all of that is going to be text (and a more complicated syntax to work with).

From what I can tell from the code you have posted, this is doing nothing more than looping over two countries and one period and outputting a couple of new data elements all of which can be done in a simple datastep:

data _null_;
  do country="UK","DE";
    do cycle=1 to 1;
      a=...;
      b=...;
      ...
      put _all_;
    end;
  end;
run;

Note I didn't bother copying all the calculation code over.  It just seems to me like your complicating an otherwise simple thing.

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 12 replies
  • 188 views
  • 1 like
  • 4 in conversation