SAS Macro Tutorial: Step-by-Step Guide with Examples, Functions, and Best Practices
  • by Handson
  • October 31, 2025
SAS Macro Tutorial: Step-by-Step Guide with Examples, Functions, and Best Practices

SAS Macro Tutorial — From Zero to Confident

1) Why the Macro Facility?

  • What it is: A text substitution and code-generation system that runs before SAS compiles/executes DATA/PROC steps.

  • What it’s for: Reuse, parameterization, looping/branching to generate SAS code dynamically.


2) Macro Variables

2.1 Create & Use

%let lib=work;
%let ds=sales2025;
proc print data=&lib..&ds(obs=5); run;

2.2 Automatic Macro Variables

Examples: &sysdate9, &systime, &sysuserid, &sysver, &syslast

%put NOTE: Today is &sysdate9 &systime., SAS &sysver, last=&syslast;

2.3 Create in DATA/PROC

data _null_;
  call symputx('nobs', 123);
run;
%put &=nobs;

From PROC SQL:

proc sql noprint;
  select count(*) into :nrows trimmed from sashelp.class;
quit;
%put &=nrows;

3) Macro Programs

3.1 Define/Call

%macro topN(lib=work, ds=, n=10);
  %if %superq(ds)= %then %do;
    %put ERROR: ds= is required.;
    %return;
  %end;

  proc sort data=&lib..&ds out=_tmp; by descending _all_; run;
  proc print data=_tmp(obs=&n); title "Top &n from &lib..&ds"; run;
%mend;

%topN(ds=sashelp.class, n=5)

3.2 Parameters

  • Positional or Named (with defaults)

  • Use %superq() to test safely.

3.3 Scope

%let g=global;
%macro scope();
  %local g;
  %let g=local;
  %put INSIDE: &=g;
%mend;
%scope()
%put OUTSIDE: &=g;

4) Macro Logic & Loops

IF/ELSE

%macro rpt(type=summary);
  %if &type=summary %then %do;
    proc means data=sashelp.class; run;
  %end;
  %else %if &type=detail %then %do;
    proc print data=sashelp.class; run;
  %end;
%mend;

DO Loop

%macro repeat(n=3);
  %do i=1 %to &n;
    %put Iter &i;
  %end;
%mend;
%repeat(n=5)

5) Macro Functions

  • %upcase(), %substr(), %scan()

  • %eval() vs %sysevalf()

  • %sysfunc() to call DATA step functions

%let today=%sysfunc(today(), date9.);
%put &today;

6) Quoting

%let val=Tom & Jerry;
%put BAD: &val;
%put GOOD: %superq(val);

7) Dynamic Code Generation

Loop over values

%let months=JAN FEB MAR;
%macro run3;
  %do i=1 %to %sysfunc(countw(&months));
    %let word=%scan(&months,&i);
    proc sql; create table out_&word as
      select * from mylib.txn_&word; quit;
  %end;
%mend; %run3

Loop over datasets

proc sql noprint;
  select memname into :dsets separated by ' '
  from dictionary.tables
  where libname='SASHELP' and memtype='DATA' and upcase(memname) like 'C%';
quit;

%macro printAll;
  %do i=1 %to %sysfunc(countw(&dsets));
    %let ds=%scan(&dsets,&i);
    proc print data=sashelp.&ds(obs=5); title "&ds"; run;
  %end;
%mend; %printAll

8) Passing Data Values into Macros

DATA Step → Macro Var

data _null_;
  set sashelp.class end=last;
  retain list '';
  list=cats(list, ifc(list='','',','), name);
  if last then call symputx('names', list);
run;
%put &=names;

PROC SQL INTO

proc sql noprint;
  select name into :names separated by ' ' from sashelp.class;
quit;
%put &=names;

9) Debugging

options mprint mlogic symbolgen;
  • MPRINT shows generated code.

  • SYMBOLGEN shows macro var resolutions.

  • MLOGIC shows macro flow.


10) Autocall Libraries

options mautosource sasautos=(sasautos "C:macros");

11) Best Practices

  • Use %superq() for safe checks.

  • Keep macros parameterized with defaults.

  • Debug with MPRINT/MLOGIC.


12) Worked Examples

Import CSVs

%macro import_csvs(path=, outlib=work, ext=csv);
  %local rc did mem i n f;
  %let rc=%sysfunc(filename(fref,"&path"));
  %let did=%sysfunc(dopen(&fref));
  %if &did=0 %then %do; %put ERROR: Cannot open &path; %return; %end;

  %let n=%sysfunc(dnum(&did));
  %do i=1 %to &n;
    %let f=%sysfunc(dread(&did,&i));
    %if %upcase(%scan(&f,-1,.))=%upcase(&ext) %then %do;
      %let mem=%scan(&f,1,.);
      proc import datafile="&path/&f" dbms=csv out=&outlib..&mem replace; guessingrows=max; run;
    %end;
  %end;
%mend;

Yearly Report

%macro yearly_report(start=2021, stop=2025);
  %do y=&start %to &stop;
    proc sql;
      create table yr&y as
      select "&y"d as year format=year4., *
      from sales.txn_&y;
    quit;
  %end;

  data all_years; set %do y=&start %to &stop; yr&y %end;; run;
  proc means data=all_years n mean sum; class year; var amount; run;
%mend;

13) Interview Concepts

  • Macro executes before DATA/PROC compile.

  • %eval vs %sysevalf.

  • Macro quoting prevents premature resolution.

  • Use SYMPUTX over SYMPUT.

  • %sysfunc bridges data step functions.


14) Exercises

  1. Build %freq(lib=, ds=, var=, where=).

  2. Multi-export macro for datasets.

  3. Dynamic WHERE clause with name list.


15) Cheat Sheet

%let x=1; &x
%if &x>0 %then %do; ... %end;
%do i=1 %to 10; ... %end;
%eval(2+3)  %sysevalf(3.14*2)
%sysfunc(today(),date9.)
%superq(name)
options mprint mlogic symbolgen;