Reputation: 13581
I want to conditionally execute macro function dependent on existence of table
Data
data have;
do i = 1 to 5;
output;
end;
run;
Table I want to condition on
data counttbl;
infile datalines delimiter='::';
format variable $char400. condition $char400.;
input variable $ condition $;
datalines;
runcount::i>1
;
run;
Some tests to show that I can condition on counttbl
existence (works as expected)
data _null_;
call execute("data want;");
call execute("set have;");
if exist("work.counttbl") then
call execute("tmp = 1;");
call execute('run;');
stop;
run;
The above creates column tmp = 1
proc delete data=work.counttbl;
run;
data _null_;
call execute("data want;");
call execute("set have;");
if exist("work.counttbl") then
call execute("tmp = 1;");
call execute('run;');
stop;
run;
After deleting the table, the above does not create the column tmp
Macro function to execute
%macro apply_change();
call execute('if _n_ eq 1 then do;');
do until (eof);
set counttbl (keep=variable) end=eof;
call execute(strip(variable) || ' = 0;');
end;
call execute('end;');
call missing(eof);
do until (eof);
set counttbl end=eof;
call execute(strip(variable) || ' + (' || strip(condition) || ');');
end;
call missing(eof);
%mend apply_change;
Works fine when counttbl
exists
data _null_;
call execute("data want;");
call execute("set have;");
if exist("work.counttbl") then
%apply_change()
call execute('run;');
stop;
run;
Throws error when counttbl
is deleted - I want it to simply skip executing the macro function
proc delete data=work.counttbl;
run;
data _null_;
call execute("data want;");
call execute("set have;");
if exist("work.counttbl") then
%apply_change()
call execute('run;');
stop;
run;
ERROR: File WORK.COUNTTBL.DATA does not exist.
Thanks in advance for your help
Upvotes: 0
Views: 649
Reputation: 51566
You cannot use a test at run time to prevent SAS from compiling some lines of code in the same data step. Instead you need to use macro logic to not generate the lines of code.
It looks like you want to use the dataset to generate a series of variables that count how many times a condition is met. I find that it is much easier to debug if that type of data driven code generation is done by writing the code to a file. Then you can stop after generating the file and look at the generated code and make sure your code generation step is working properly.
It looks like you want the new dataset generated whether or not the file with the list of VARIABLE/CONDITION pairs exists or not. So just hard code that part of the data step and only conditionally generate the part that calculates the new variables. Since you are generating sum statements there is not need for the IF _N_=1 block to set the initial values to zero. SAS will automatically set them to zero and retain them. (Assuming that HAVE doesn't already have variables with those names, in which the sum statement won't work right either.)
filename code temp;
data _null_;
file code ;
%if %sysfunc(exist(&dsname)) %then %do;
set &dsname end=eof;
put ' ' variable '+ ( ' condition ');' ;
%end ;
run;
So either the temp file CODE is empty or it has code like:
VAR1 + ( dx='123' );
VAR2 + ( sex='M' );
Then to make your dataset just run this step with a %INCLUDE
to add in the conditionally generated code.
data want;
set have;
%include code /source2;
run;
If you are using an old version of SAS you will need to wrap that %IF
statement into a macro. But the newest releases of SAS allow that type of simple %IF/%THEN/%DO/%END construct in open code.
Upvotes: 1
Reputation: 27498
Your problem area is
if exist("work.counttbl") then
%apply_change()
Macro is processed, and generates source code, prior to the SAS system implicitly compiling the data step and running it.
I would not recommend pursuing this avenue because it mixes scopes (macro/data step)
If you persist, there are a couple of tips
SET counttbl
when not presentFor example:
%macro apply_change();
%if not %sysfunc(EXISTS(WORK.COUNTTBL)) %then %return;
%* This code gen only done when COUNTTBL present;
call execute('if _n_ eq 1 then do;');
do until (eof);
set counttbl (keep=variable) end=eof;
call execute(strip(variable) || ' = 0;');
end;
call execute('end;');
call missing(eof);
do until (eof);
set counttbl end=eof;
call execute(strip(variable) || ' + (' || strip(condition) || ');');
end;
call missing(eof);
%mend apply_change;
Replace
if exist("work.counttbl") then %apply_change()
With
%apply_change()
Upvotes: 2
Reputation: 1427
First
if exist("work.counttbl") then
will only apply to the first line of your macro
call execute('if _n_ eq 1 then do;');
This is because the macro is evaluated before the datastep is executed. So sas will simple paste the macro content to the location of the macro invocation. But even when it would apply to the whole macro it will not work. Take for example the following code:
data x;
if 0 then do;
set y;
end;
set z;
run;
Her y and z has to exist. However no observation will be read from y only structure is taken.
Upvotes: 1