Hello,
Just trying to learn loops, can't figure out why it keeps giving me this error even though there are no unclosed loops. Thanks in advance for pointing my mistake out.
data main;
infile datalines;
input acc $19.;
obs=_n_;
datalines;
12344567969705
56754846834684
12344567969715
56754846834694
56754846834696
;
run;
proc rank data=main out=main1 groups=2;
var obs;
ranks rank;
run;
%macro create (n);
data main_&n.;
set main1;
if rank = &n. then output;
run;
%mend;
data _null_;
i=0;
do until (i=2);
%create(i);
i=i+1;
end;
run;
Log:
NOTE: The SAS System stopped processing this step because of errors.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
NOTE: Line generated by the invoked macro "CREATE".
2174 data main_&n.; set main1; if rank = &n. then output; run;
-
117
ERROR 117-185: There was 1 unclosed DO block.
NOTE: Variable i is uninitialized.
NOTE: There were 5 observations read from the data set WORK.MAIN1.
NOTE: The data set WORK.MAIN_I has 0 observations and 4 variables.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds
2175 i=i+1;
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
2176 end;
---
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
That's great, except if usually does not make any sense to create a macro that uses "magical" macro variables that appear from no where when they are clearly parameters. Pass the value when calling the macro.
call execute (cats('%nrstr(%create)(n=',i,')'));
You cannot nest data steps, but that is what your macro does. The "data" statement created by the macro ends the "outer" step before the "end" statement could be compiled.
If you are just learning loops don't complicate things by adding macros. The way you have written it you are trying to run one DATA step inside another which is not allowed in SAS. Remove the macro call and add a simple SAS statement:
data _null_;
i=0;
do until (i=2);
%create(i); * <== remove this;
put i=; <== add this;
i=i+1;
end;
run;
@AJ_Brien: Adding to all the above good comments
Macros perform text substitution — when the program runs, the macro elements of the code are replaced by the values of the macro variables or the values of the macro, and once this text substitution happens, you MUST have valid legal working SAS code, which you do not have.
When writing macros, or using macro variables, you first need to write some code that works for one or two instances WITHOUT macros and WITHOUT macro variables. If you had done that, you would know the problem isn't the macros, the problem is that your code WITHOUT macros and WITHOUT macro variables has errors. Once you have non-macro code without errors, then you can go ahead and insert macros or macro variables, and have a reasonable likelihood that the code will work with macros and with macro variables.
It looks like the problem has been well identified, but the solutions are not really sufficient.
Change two pieces to your program. First, get rid of the macro parameter N on %create, so it just reads:
%macro create;
The definition of the macro does not otherwise change, and should still reference &n. However, &n will be set using different methods.
Then address the problem DATA step:
data _null_;
i=0;
do until (i=2);
%create(i);
i=i+1;
end;
run;
Replace that with:
data _null_;
i=0;
do until (i=2);
call symputx('n', i) ;
call execute ('%create');
i=i+1;
end;
run;
There are tricky reasons why this will work, that require good understanding of CALL EXECUTE.
That's great, except if usually does not make any sense to create a macro that uses "magical" macro variables that appear from no where when they are clearly parameters. Pass the value when calling the macro.
call execute (cats('%nrstr(%create)(n=',i,')'));
For those of you familiar with CALL EXECUTE, I would suggest that you look into the DOSUBL routine as an alternative to EXECUTE. EXECUTE generates code which runs after the DATA step finishes. DOSUBL runs the code immediately.
All of the comment above are spot-on as to the issue - the fact that macro is a code generator.
It looks like the intent is to use a data step to loop thru the distinct values (instead of a macro %do loop) and create a separate data set for each value.
If so, a more direct approach would be a macro like this:
%macro split(data=
,var=
);
proc sql noprint;
/* create an "array" (value1, value2,...) of distinct values */
select distinct &var into:value1- from &data;
quit;
%local i;
data %do i = 1 %to &sqlobs; main_&&value&i %end;
;
set &data;
select (&var);
%do i = 1 %to &sqlobs;
when (&&value&i) output main_&&value&i;
%end;
end;
run;
%mend split;
which can be called for this case as follows:
options mprint;
%split(data=main1,var=rank)
I included the mprint option so the generate code is shown in the log:
MPRINT(SPLIT): proc sql noprint;
MPRINT(SPLIT): select distinct rank into:value1- from main1;
MPRINT(SPLIT): quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
MPRINT(SPLIT): data main_0 main_1 ;
MPRINT(SPLIT): set main1;
MPRINT(SPLIT): select (rank);
MPRINT(SPLIT): when (0) output main_0;
MPRINT(SPLIT): when (1) output main_1;
MPRINT(SPLIT): end;
MPRINT(SPLIT): run;
NOTE: There were 5 observations read from the data set WORK.MAIN1.
NOTE: The data set WORK.MAIN_0 has 2 observations and 3 variables.
NOTE: The data set WORK.MAIN_1 has 3 observations and 3 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds
This code is a variable of one of the example covered in the current SAS Macro Language course.
Disclaimer: this code is not intended as a bulletproof macro - just a POC.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.