Macros Tip: %INCLUDE vs. Macro Language

1
Robert Virgile
Robert Virgile

This SAS author tip is from Robert Virgile, author of “SAS Macro Language Magic: Discovering Advanced Techniques”.  It actually came about when a reader posted a comment on one of Virgile’s blogs. Thank you to that reader for their comment!

Technically, %INCLUDE is not part of macro language.  Yes, it begins with a percent sign, but it is still a different tool.  What they share: for some applications, %INCLUDE is an alternative to macro language.

First, let’s review a few basics.  %INCLUDE always references a file.  Two typical examples:

%include 'complete path to a file';
 
filename somename 'complete path to a file';
%include somename;

The rules are simple:

  • The referenced file must contain complete SAS statements, not pieces of statements.
  • Those SAS statements must make sense, when inserted into a SAS program at the point where the %INCLUDE statement appears.

That’s all %INCLUDE does.  It inserts a set of SAS statements into the program, at the proper point.

Next, let’s examine some macro language applications where %INCLUDE might be useful.  In all of the examples that follow, assume that a single folder defines macros in typical fashion:

  • The member sales.sas contains the definition of %sales.
  • The member prices.sas contains the definition of %prices.
  • Et cetera.

One common misapplication of %INCLUDE is to define all macros that the current program will use:

%include '/macro/library/sales.sas';

That enables the current program to utilize %sales.  However, this approach embodies certain drawbacks, especially if there are many %INCLUDE statements:

  • The approach is subject to error. It is possible that %sales calls additional macros that also need to be defined using %INCLUDE.
  • When a program utilizes many macros, maintenance becomes an issue. There is more to type originally, and more to QC, in order to verify that all macros are coming from the same folder.

These drawbacks are mild, and some programmers utilize %INCLUDE in this way.  However, it is much clear and simpler to define a folder as holding available macros:

options sasautos=('path to a folder holding macro definitions');

Now all macros in that folder will be available to the program, without mentioning each macro individually.  Other styles exist, such as creating a single file that holds all macro definitions … and each style has its pluses and minuses.  The SASAUTOS option generally embodies the best ratio of features to drawbacks.

Finally, let’s examine an application where %INCLUDE has advantages over macro language.  The intent: for each of a set of variables, create an output data set from PROC FREQ.  For example, begin with this set of variables:

%let varlist = department jobtitle tenure;

The program must continue by generating this code:

proc freq data=employees;
   tables department / out=department;
   tables jobtitle / out=jobtitle;
   tables tenure / out=tenure;
run;

Macro language can accomplish this.  A complete macro definition/invocation might look like this:

%macro table_list (varlist=);
   proc freq data=employees;
   %local i nextname;
   %do i=1 %to %sysfunc(countw(&varlist));
       %let nextname = %scan(&varlist, &i);
       tables &nextname / out=&nextname;
   %end;
   run;
%mend table_list;
%table_list (varlist=department jobtitle tenure)

However, this approach illustrates two of the difficulties with macro language:

  • Macros can clutter up the log, and
  • If there is an error, it may more difficult to decipher. Macro language error messages do not point out the exact place where a SAS language error occurs.

This is a case where %INCLUDE is a viable alternative.  The approach:  have a DATA step write all the TABLES statements to a file, then %INCLUE that file at the proper point in the program.  Here is what the alternative might look like:

%let varlist = department jobtitle tenure;
data _null_;
  file 'tables.sascode';
  length nextword $ 32;
  do i=1 to countw("&varlist");
     nextword = scan("&varlist", i);
     put '   tables ' nextword '/ out=' nextword ';';
  end;
run;

When this DATA step runs, it writes three SAS statements to tables.sascode:  the three required TABLES statements:

   tables department / out=department ;
   tables jobtitle / out=jobtitle ;
   tables tenure / out=tenure ;

All that remains is to include those statements within PROC FREQ:

proc freq data=employees;
  %include 'tables.sascode';
run;

This approach has two advantages:

  • If an error exists in the generated code, it will be easier to decipher. SAS language error messages (as opposed to macro language error messages) do a much better job of pointing to the exact location of the error.
  • For future reference, a file exists holding all of the TABLES statements.

Not only does %INCLUDE have advantages over macro language, but notice a trick that makes this approach even more useful.  The trick relies on the fact that a batch job is capable of retrieving the path to the source program, using:

%sysfunc(getoption(sysin))

Therefore, a small modification to the FILE statement creates an interesting result:

file "%sysfunc(getoption(sysin))code";

The original program might have been stored in a folder, using the name prog1.sas.  Automatically, the three TABLES statements get written to a matching name within the same folder:  prog1.sascode.  Just like log and lst files match the folder and program name, you can write to your own output file using the same folder and program name.

We hope you found this tip useful and you can also read an excerpt from Virgile’s book for more on the macro language.

Share

About Author

Cindy Puryear

Senior Marketing Specialist, SAS Publications

1 Comment

  1. Kevin Elliott on

    Good article. Thanks!

    I've banned %include for macros in our live code because of the maintenance issues. Especially when some clever programmers nest %includes. Autocalls are generally preferable. And you always know where the source is (unless you have more than one macro library).

    One thing I've found useful - if you're using autocalls, but developing and testing macors in an online session and use a better featured editor outside the session - %include is a good way to bring in the new macro code version. I've seen no end of guys get caught because the autocall only works first time around.

Back to Top