Can you avoid && ..%%?

From sasCommunity
Jump to: navigation, search

"Things should be made as simple as possible, but not simpler." — Albert Einstein (1879–1955)


Some macro fans like to create "complicated" macro code with tons of && and %%. Actually, in lots of cases, things can be made simpler.

Here is an example I found on SAS-L:

    TASK:
       *to merge 2 data (month prior and subsequent month). 
       For example, merge data sale200604 with nosale200605. 
       The time frame is from 200604 thru 200705.;
    NOTE: This code has bugs. The author said, 
          "The following code is ok except data sale200612.";
    CODE:
         %macro mer(st1,ed1);
	     %do i=&st1. %to &ed1.;
		data drop&i.;
		merge sale&i. (in=a) nosale%eval(&i**1*+*1*) (in=b);
		by acct_num;
		if a and b;
		mth_key=(&i**1*+*1*);
		run;
		%let i=%eval(%sysfunc(putn(%sysfunc(intnx(month,
               %sysfunc(inputn(&i,yymmn6.)),1)),yymmn6.))-1);
	    %end;
          %mend mer;
          %mer(200604, 200705);     
    
     ISSUE of &&..%%;
     %let i=%eval(%sysfunc(putn(%sysfunc(intnx(month,
                  %sysfunc(inputn(&i,yymmn6.)),1)),yymmn6.))-1);
    LINK to the SAS-L thread: [1]

I didn't take any trouble to debug the code. Instead, I tried to make the things as simple as possible.

I offered to "think in SAS", which I meant SAS is data-oriented programming language, you can use data steps to generate code. That will make the logic more clear and the code easier to debug and maintain.

My suggested code is as follows:

1. During development phase you can use an extra data step to see the intermediate results in the data set _DATE.

     	data _date;
		from='01jan2006'd;
		to  ='01sep2007'd;

		t_pair = INTCK('month',from,to)-1;
		 
		do pair=0 to t_pair;
		start=intnx('month',from,pair);
		end=intnx('month',from,pair+1);
		dsn1='sale'||put(start,yymmn6.);
		dsn2='nosale'||put(end,yymmn6.);
		output;
		end;
		format start end date.;

	run;

	data _null_;
		set _date (obs=1 keep=dsn1 dsn2);
		call execute(' data '|| dsn1||'_'||dsn2||';
		             merge '|| dsn1 ||' '||dsn2 ||';
		             by  acc_id; run;'
		             );
	run;  

You can then convert it to a macro version as follows:

  		%macro merge_pair(from,to);
		data _date;
		from=&from;
		to  =&to;

		t_pair = INTCK('month',&from,to)-1;
		 
		do pair=0 to t_pair;
		start=intnx('month',&from,pair);
		end=intnx('month',&from,pair+1);
		dsn1='sale'||put(start,yymmn6.);
		dsn2='nosale'||put(end,yymmn6.);
		output;
		end;
		format start end date.;

		run;

		data _null_;
		set _date (obs=1 keep=dsn1 dsn2);
		call execute(' data '|| dsn1||'_'||dsn2||';
		             merge '|| dsn1 ||' '||dsn2 ||';
		             by  acc_id; run;'
		             );
		run;
		%mend;

		%merge_pair('01nov2006'd,'01feb2007'd);

2. After you tested your code with a sample of data and got expected results you can tune it into the production version as follows:

   %macro merge_pair(from,to);
		data _null_; 
			t_pair = INTCK('month',&from,&to)-1; 
			do pair=0 to t_pair;
			start=intnx('month',&from,pair);
			end=intnx('month',&from,pair+1);
			dsn1='sale'||put(start,yymmn6.);
			dsn2='nosale'||put(end,yymmn6.);
			call execute(' data '|| dsn1||'_'||dsn2||';
			             set '|| dsn1 ||' '||dsn2 ||';
			             by  acc_id; run;'
			             );
			end;
		run;
	%mend;

	%merge_pair('01nov2006'd,'01feb2007'd);

Comments are welcome!!