Articles
SAS Macro Interview Questions: Resolution Timing and Quoting Mistakes
Prepare for SAS macro interview questions with resolution traces, DATA step timing, indirect references, and quoting choices that prevent rescanning errors.
A SAS program assigns FRESH with CALL SYMPUTX, yet an earlier-looking reference in the same DATA step prints STALE. Another macro receives the label A&B and unexpectedly tries to resolve B as a macro variable. These are different failures: the first crosses an execution-phase boundary; the second exposes data to another macro scan.
For SAS macro interview questions, trace when text is processed, which symbol table supplies a value, and which characters remain active. Macro quoting is not ordinary string quoting, and it does not make every reference wait until a DATA step executes.
Evidence boundary: The traces below are original, documentation-supported exercises, not logs from an executed SAS session or reported employer questions. The linked official references describe conventional SAS 9.x macro behavior and were checked on October 1, 2026; they do not establish CAS equivalence. Run the snippets in your permitted SAS environment before describing their output as an observed result.
To connect text-generation reasoning with analytical work, use the verified Data Analyst practice collection.
Explain the two languages before diagnosing the bug
The macro facility processes macro statements and references to produce text. SAS then compiles and executes the resulting SAS statements. A macro variable is not a DATA step variable, and a macro loop is not a loop over observations simply because it appears near a SET statement.
That distinction changes the debugging question. Instead of asking only “What value does this variable have?”, ask “Which processor reads this token, at what point, and what text reaches the next processor?” SAS's program and macro-processing reference explains the logical relationship between the processors.
For example, %let suffix=west; followed by proc print data=sales_&suffix; produces a statement referring to sales_west. That does not prove the dataset exists. Successful macro resolution and successful SAS execution are separate checks.
An interview answer should name both outputs: the generated statement and the behavior of that statement. A perfectly resolved column name can still be absent from the input dataset. Conversely, an unresolved reference can make valid-looking source code generate invalid SAS syntax.
Why does the same DATA step see an old macro value?
Start from a deliberately initialized global value, so the example distinguishes stale resolution from an undefined-variable warning:
%let status=STALE;
data _null_;
call symputx('status','FRESH','G');
put "inside=&status";
run;
%put after=&status;
The expected trace is inside=STALE, followed by after=FRESH. The double-quoted macro reference is resolved while the DATA step is being prepared for execution. At that point, the symbol table still holds STALE. CALL SYMPUTX executes later and updates the global entry; it does not rewrite the previously compiled PUT string. SAS explicitly warns about creating and referencing a macro variable within the same DATA step. SAS DATA step timing explanation
The third CALL SYMPUTX argument makes the intended global destination explicit. SAS's sample documents its trimming behavior and optional symbol-table selection. Those conveniences do not change when the routine executes. CALL SYMPUTX sample
Do not “fix” this by placing &status in single quotation marks. That generally leaves the literal macro reference in the SAS string; it is not a delayed request for a future macro scan. Quote choice and execution timing solve different problems.
If the next program step needs the value, keep a real step boundary before the macro reference. If the current DATA step needs a value during execution, use an execution-time interface rather than expecting compile-time substitution to observe a later assignment.
How does SYMGET change the timing contract?
Here is the corresponding runtime read:
%let status=STALE;
data _null_;
length observed $ 12;
call symputx('status','FRESH','G');
observed=symget('status');
put observed=;
run;
The expected observed value is FRESH because SYMGET retrieves the named macro variable during DATA step execution, after the assignment in this simple sequential step. Its argument is the variable name, not &status. SAS also states that SYMGET does not recursively resolve another macro reference stored inside the retrieved value. SAS SYMGET reference
Specifying the DATA step character length makes the receiving field's capacity visible. In a larger program, check whether a value can be truncated, whether the named variable exists, and which scope is visible. The runtime interface is not a substitute for a data contract.
If a dataset has many observations, CALL SYMPUTX may execute repeatedly. Ask whether the intended result is the first row, last row, an aggregate, or one named variable per key. A single global variable repeatedly overwritten is not a collection. In the interview, explain the desired cardinality before inserting a routine inside a row loop.
Trace an indirect reference one scan at a time
Suppose the program selects a region-specific value:
%let i=2;
%let region2=WEST;
%put selected=&®ion&i;
During the first scan, && supplies a delayed ampersand, while &i resolves to 2. The intermediate reference becomes ®ion2. Rescanning resolves that named variable to WEST. SAS documents this left-to-right indirect-resolution mechanism. Indirect macro references
The useful asset is the intermediate text, not the final word alone. Write it on paper: source &®ion&i, intermediate ®ion2, result WEST. Then change i to 3 while leaving region3 undefined. Your explanation should predict a resolution problem instead of inventing a fallback value.
Also separate a delimiter from a desired output period. In a reference such as &prefix._sales, the period ends the macro variable name; it is not necessarily copied into the generated text. If the output itself needs a period, make that distinction explicit. Ambiguous name boundaries can look like a scope failure when the processor is actually searching for a longer variable name.
Avoid treating more ampersands as a universal repair. Each extra scan can activate text that was supposed to remain data. Indirect resolution is appropriate when choosing another variable name is intentional, not when a customer label merely happens to contain an ampersand.
Which macro quoting function belongs at which boundary?
Macro quoting masks characters from interpretation by the macro processor. The right function depends on whether the troublesome text is present when a macro is compiled or arrives during execution, and whether ampersands and percent signs must remain inactive. SAS provides a quoting-function decision guide.
Use this comparison as a reasoning aid, not a memorized replacement for tracing the input:
| Quoting family | Boundary to explain |
|---|---|
%STR / %NRSTR | Protect source text during macro compilation; the NR form also masks ampersands and percent signs. |
%BQUOTE / %NRBQUOTE | Protect execution-time text; BQUOTE handles unmatched quotation marks and parentheses without source marking, while the NR form additionally masks macro triggers. |
%SUPERQ | Retrieve an existing macro variable's value without trying to resolve macro triggers inside it. |
“NR” does not mean that everything in the surrounding program is deferred. Nor does execution-time quoting mean the argument was never evaluated. In particular, wrapping an already active &label reference in NRBQUOTE can be too late to prevent resolution while its argument is obtained.
For source text, %nrstr(A&B) intentionally preserves a literal ampersand. For a value arriving from a dataset or external input, reason about the retrieval step too. You need to prevent unwanted interpretation before it occurs, not merely mask what remains afterward.
Ordinary SAS quotes still matter to the SAS compiler. Macro quoting and SAS string syntax protect different interpretations. A value safe from a macro rescan can still require careful handling when inserted into a SAS literal, dataset name, or SQL statement.
Why does SUPERQ take a name without an ampersand?
Consider a literal label introduced through a DATA step:
data _null_;
call symputx('label','A&B','G');
run;
%put label=%superq(label);
The expected label is the literal text A&B. %superq(label) receives the name of the macro variable and retrieves its stored value without attempting to resolve B. %superq(&label) requests a different operation by expanding the argument before retrieval. That defeats the intended boundary and may produce warnings or look for the wrong name. SAS SUPERQ reference
The single-quoted DATA step input in this example is intentional: A&B is a SAS character literal, not a requested macro reference. Once stored, SUPERQ protects its use in the macro facility. Do not generalize the example into a claim that arbitrary text can be inserted safely anywhere in generated code.
A good follow-up is: “What happens if the label contains a comma or unmatched parenthesis?” Explain which processor would otherwise treat it as a delimiter and where masking belongs. Another is: “What if the value is meant to be a macro expression?” Then activating it may be intentional, but that is a different and more dangerous input contract.
Check repeatability as well as the first successful call. A previous test can leave global macro values in the session, making a missing assignment look correct on the next run. Initialize the fixture deliberately, use a fresh or controlled scope, and verify behavior when a required name is absent. Otherwise you may demonstrate remembered session state rather than the code's actual input-to-output contract.
Treat externally supplied values as data unless the program explicitly authorizes code generation from them. Quoting does not validate an identifier, authorize a dataset, or make untrusted program text harmless. Use an allowlist for permitted columns or tables when those values influence generated statements.
Diagnose the generated statement without leaking the input
SAS documents three particularly useful debug options: MPRINT shows SAS statements generated by macros, MLOGIC traces macro execution decisions, and SYMBOLGEN traces variable resolution. Choose the option that addresses the hypothesis rather than enabling every trace indefinitely. SAS macro debugging techniques
For the indirect-reference case, resolution tracing is relevant. For a malformed generated PROC statement, inspect the generated text. For a wrong branch in a macro condition, inspect the macro logic. None of these alone proves that the final dataset has the intended rows.
Use synthetic labels in a minimal reproduction. Debug output can expose parameters, resolved text, or generated statements containing sensitive values. A protected value can still be printed by diagnostics. Avoid dumping all user macro variables from a shared session, and turn tracing off after the investigation.
Your recovery explanation should distinguish three checks: the resolved macro value, the generated SAS statement, and a small expected dataset or row count. If the first two are correct but a join drops rows, investigate ordinary data-processing semantics rather than applying more macro quoting.