6.5 Simplifying Programs with Macro Variables (%LET)
Key Takeaways
- %LET creates a macro variable whose value is always stored as text, with leading and trailing blanks stripped and any quotation marks kept literally.
- An ampersand reference such as ®ion substitutes the stored text into the program before the DATA or PROC step compiles.
- Macro variables resolve inside double quotation marks but never inside single quotation marks.
- The macro variable name delimiter is a period: &prefix.2026 resolves the variable named prefix and the delimiting period is consumed rather than printed.
- OPTIONS SYMBOLGEN and the %PUT statement expose what a macro variable actually resolved to, which is the fastest way to diagnose an unresolved reference.
6.5 Simplifying Programs with Macro Variables (%LET)
Quick Answer: The content guide requires you to "use macro variables to simplify program maintenance" through three expanded objectives: create macro variables with the
%LETstatement, use macro variables within SAS programs, and use the macro variable name dot delimiter (.). Base-level macro work is text substitution and nothing more — no%MACROdefinitions are required for A00-231.
A macro variable holds text. Before SAS compiles a DATA or PROC step, the macro processor scans the source, finds every &name reference, and replaces it with the stored text. The DATA step never knows a macro variable existed; it sees only the substituted result.
1. Creating Macro Variables with %LET
%LET macro-variable-name = value;
%let reportYear = 2026;
%let region = West;
%let inLib = sasuser;
Four rules govern what actually gets stored:
- Everything is text.
%let reportYear = 2026;stores the four characters2026, not the number two thousand twenty-six. SAS treats the substituted text as a number only when the surrounding SAS code puts it in a numeric context. - Leading and trailing blanks are stripped.
%let region = West ;storesWest. - Quotation marks are stored literally.
%let region = 'West';stores'West'including the quotes, which then appear in the substituted code — a classic source of doubled quotation marks. - The name follows SAS naming rules — 1 to 32 characters, beginning with a letter or underscore.
/* One edit changes the whole program */
%let region = West;
data work.filtered;
set sashelp.shoes;
where Region = "®ion"; /* becomes: where Region = "West" */
run;
proc means data=work.filtered mean sum;
var Sales;
title "Sales Summary for ®ion";
run;
Change the %LET to Africa and every downstream reference follows. That is precisely the "simplify program maintenance" the objective is describing.
2. Referencing Macro Variables: the Quotation Mark Rule
The macro processor scans double-quoted strings but skips single-quoted strings entirely.
| Code | Result |
|---|---|
title "Report for ®ion"; | Report for West — resolved |
title 'Report for ®ion'; | Report for ®ion — literal text |
where Region = "®ion"; | where Region = "West" — correct |
where Region = '®ion'; | Compares Region to the literal string ®ion, matching nothing |
Exam Trap: this is the most-tested macro fact at Base level. Whenever a macro reference must resolve inside a quoted string — a
TITLE, aFOOTNOTE, aWHEREclause, a file path — the string must use double quotation marks.
3. The Name Delimiter: The Period
A macro variable reference ends at the first character that cannot be part of a SAS name. That works for ®ion/sales, but it fails when text must butt directly against the value:
%let prefix = sales;
/* WRONG: SAS looks for a macro variable named PREFIX2026 */
data &prefix2026; /* WARNING: Apparent symbolic reference PREFIX2026 not resolved */
run;
/* RIGHT: the period terminates the name and is then consumed */
data &prefix.2026; /* resolves to: data sales2026; */
run;
The delimiting period is removed during resolution. When the generated text needs a period of its own, code two periods:
%let lib = sasuser;
%let ds = clients;
proc print data=&lib..&ds; /* resolves to: proc print data=sasuser.clients */
run;
The first period ends &lib; the second survives as the two-level-name separator. Writing &lib.&ds produces the single token sasuserclients and an error.
%let yr = 2026;
%let ext = csv;
/* One delimiter period plus one literal period in a file name */
filename out "C:\reports\summary_&yr..&ext"; /* summary_2026.csv */
4. Automatic Macro Variables
SAS maintains a set of read-only macro variables that resolve without any %LET:
| Variable | Contains | Example |
|---|---|---|
&SYSDATE | Session start date, DATE7. | 07AUG26 |
&SYSDATE9 | Session start date, DATE9. | 07AUG2026 |
&SYSTIME | Session start time | 09:15 |
&SYSDAY | Day of the week | Friday |
&SYSUSERID | Operating-system user ID | rchen |
&SYSLAST | Most recently created data set | WORK.FILTERED |
&SYSERR | Return code of the most recent step | 0 |
footnote1 "Produced by &SYSUSERID on &SYSDAY, &SYSDATE9";
Note that &SYSDATE and &SYSTIME capture the moment the session started, not the moment the statement runs, so a long batch job stamps every page with the same value.
5. Debugging Macro Resolution
Because substitution happens before compilation, a macro bug surfaces as a puzzling DATA step error rather than a macro error. Three tools make the substitution visible.
options symbolgen; /* log every macro variable resolution */
%let region = West;
%put NOTE: region resolves to ®ion; /* writes directly to the log */
%put _USER_; /* lists all user-defined macro variables */
SYMBOLGEN writes a line such as SYMBOLGEN: Macro variable REGION resolves to West for every reference, which immediately exposes the two most common failures: a value carrying unexpected quotation marks, and a reference that never resolved at all.
Exam Tip:
WARNING: Apparent symbolic reference X not resolvedmeans no macro variable namedXexists — usually a typo or a missing name delimiter. The step still runs, substituting the literal text&X, which is why the eventual error message often points somewhere entirely unrelated.
After %let region = West;, which statement displays the title text 'Sales Report for West'?
Given %let prefix = sales;, which reference generates the data set name sales2026?
What exactly does %let cost = 180; store in the macro variable COST?
A program logs WARNING: Apparent symbolic reference REGON not resolved and then fails in an apparently unrelated statement. What is the most likely cause and the fastest diagnostic?