This code will be part of process in SAS data Integration Studio.
I want to achieve something like:
%macro conditional_start();
%let check_condition = 0;
%if check_condition eq 0 %then %do;
%send_email_that_condition_has_been_met(); /* this I have made */
/*Below run some kind of built-in macro probably, to stop execution of the whole code below */
%end;
%mend;
/*More macros end code I don't want to execute*/
I cannot pack everything below in big "if" statement because they are built in blocks.
Is it possible?
Thanks in advance!!
Have you tried to put your code into "precode" (leave macro open) and "postcode" (rest of the macro) sections of your job's properties?
For example:
precode:
%macro the_whole_job();
postcode:
%mend the_whole_job;
%macro conditional_start();
%let check_condition = 0;
%if check_condition eq 0 %then %do;
%send_email_that_condition_has_been_met(); /* this You have made */
/*do some kind of built-in macro meaning failure, not executing the whole code above*/
%end;
%else %do;
%the_whole_job;
%end;
%mend;
Related
I am trying to solve a problem in which, based on certain conditions, it assigns you a parameter with the let function. For this exercise I am using %if with %let conditions on the code. The code I have written so far in simplified way is the following:
%let anio = 2022;
%let base = 2;
%Macro Data;
%if &anio = 2022 %then %do;
%Let year_add = %Str(&Base.C);
%Let year_add1 = %Str(&Base.B);
%mend;
%Data;
%put &=year_add;
%put &=year_add1;
The problem is that apparently the macro is not assigning any value to me in the second let statement
The first %put = &year_add gives me the correct result 2C.
Unfortunately with the second %put = &year_add1 it appears the following message: apparent symbolic referenc yeard_add1 not resolved
Can anyone can give me a hand or advise on how I can assign different let statements based on a condition?
Thanks in advance.
Your macro definition is missing an %END for the %DO.
%macro data;
%if &anio = 2022 %then %do;
%let year_add = &Base.C;
%let year_add1 = &Base.B;
%end;
%mend;
If the target macro variables, YEAR_ADD and YEAR_ADD1 do not already exist then your macro will create them as LOCAL to the DATA macro. So once the macro finishes they will be removed.
The easiest solution is just to make sure the macro variables exist before you call the macro.
%let anio = 2022;
%let base = 2;
%let year_add=;
%let year_add1=;
%data;
%put &=year_add;
%put &=year_add1;
If you are certain the macro variables do not already exist in some other macro that is calling %DATA() then you could add a %GLOBAL statement to define them in the GLOBAL macro scope so they will not be removed when the macro finishes by adding this to the macro definition:
%global year_add year_add1 ;
But that will generate an error if they have been defined as LOCAL to some other macro that called %DATA. So to be safe only force them into the GLOBAL scope if they do not already exist.
%if not %symexist(year_add) %then %global year_add;
%if not %symexist(year_add1) %then %global year_add1;
But the logic does not require you to define a macro. Just use the %IF/%THEN/%DO/%END block in open code. Then you won't have any macro variable scoping issues.
%if &anio = 2022 %then %do;
%let year_add = &Base.C;
%let year_add1 = &Base.B;
%end;
That works fine unless you are running on some really old version of SAS.
I use a macro in several SAS programs, so I defined it in a separate file /myFolder/myMacro.sas.
When running in batch, I want to use it this way: %include '/myFolder/myMacro.sas;'
When testing changes to the code in Enterprise Guide, I wan to edit and run /myFolder/myMacro.sas, then edit and run the programs that use it. How do I conditionally include the macro definitions?
%if &server = BATCH_SERVER %then %include '/myFolder/myMacro.sas;' does not work: The file is included anyway and the %if statement is applied to the comment on top of the file and results in
ERROR: Expected %DO not found.
ERROR: Skipping to next %END statement.
Just use a %then %do
%let mode=BATCH;
filename mac1 temp;
filename mac2 temp;
data _null_;
file mac1;
put '%macro mac1;%put mac1;%mend;%mac1;';
data _null_;
file mac2;
put '%macro mac2;%put mac2;%mend;%mac2';
run;
%if &mode=BATCH %then %do;
%inc mac2;
%end;
%else %do;
%inc mac1;
%end;
As I suspected, the error occurs because the include file starts with comments, somthing like:
* MyMacro is written to do this and that *;
* ************************************** *;
%macro MyMacro;
proc this;
proc that;
run;
%mend;
So after including the file, this becomes
%if &server = BATCH_SERVER %then * MyMacro is written to do this and that *;
* ************************************** *;
%macro MyMacro;
proc this;
proc that;
run;
%mend;
which is invalid.
When working inside a macro: add %do; and %end;
As Allan suggested, it is sufficient to put the %inlcude between %do; and %end;
%if &server = BATCH_SERVER %then %do;
%include '/myFolder/myMacro.sas;'
%end;
So after including the file, this becomes
%if &server = BATCH_SERVER %then %do;
* MyMacro is written to do this and that *;
* ************************************** *;
%macro MyMacro;
proc this;
proc that;
run;
%mend;
%end;
which works.
When working in open code: use call execute
data _null_;
if "&mode"="BATCH" then call execute ("%include /myFolder/myMacro.sas;");
run;
%DoIt;
I am new to SAS macro writing and I have been struggling with writing a code for the following instance.
%let DateOfInterest= "15jul2016"d;
%let yearyyyy=%sysfunc(putn(&DateOfInterest,year4.));
%let yearyyyy2=eval(yearyyyy+1);
data _null_;
if "01JAN2016"d<=&DateOfInterest<="31MAR2016"d then do;
%let reportdate="31MAR2016"d;
%let reportdate2="01APR2016"d;
%let reportdate3="01JAN2016"d;
%let QuarterOfInterest=Q1;
if "31MAR2016"d<&DateOfInterest<="30JUN2016"d then do;
%let reportdate="30JUN2016"d;
%let reportdate2="01JUL2016"d;
%let reportdate3="01APR2016"d;
%let QuarterOfInterest=Q2;
if "30JUN2016"d<&DateOfInterest<="30SEP2016"d then do;
%let reportdate="30SEP2016"d;
%let reportdate2="01OCT2016"d;
%let reportdate3="01JUL2016"d;
%let QuarterOfInterest=Q3;
if "30SEP2016"d<&DateOfInterest<="31DEC2016"d then do;
%let reportdate="31DEC2016"d;
%let reportdate2="01JAN2017"d;
%let reportdate3="01OCT2016"d;
%let QuarterOfInterest=Q4;
end;
end;
end;
end;
run;
The code runs without any problem. However, whatever DateOfInterest I choose, the reportdate variables come out to be the ones specified in the last if loop. is there a way to change the code in order to have reportdates variable in line with the DateOfInterest?
Thanks.
You're combining macro with data step in a way that doesn't work. Macro language and data step language are basically unrelated: macro language can write data step code, and the other way around, but not affect each other generally.
In particular, macro code is compiled and executed first, before any dataset is opened or any data step code is compiled or executed. That's the point really - it lets you write datastep code pre-compilation.
So
if ... then do;
%let something
end;
That doesn't work, because the macro %let happens first, then later the data step happens.
%if ... %then %do;
%let something
%end;
That works, because it's all in the macro language. Generally speaking, if it doesn't have a % at the start, it's not a macro statement/function, and won't work on the macro language.
What you're doing is going to have some more complications, though. You have to be in a macro to use %if, but you also have scoping issues.
So a general small macro like this would be:
%let mval=1;
%macro set_things;
%if &mval=1 %then %do;
%let mval1=1;
%end;
%else %if &mval=2 %then %do;
%let mval2=1;
%end;
%else %do;
%let mval0=1;
%end;
%mend;
%set_things();
%put &=mval &=mval0 &=mval1 &=mval2;
Notice that doesn't work: because it's not global, so you need one more line inside the macro:
%global mval0 mval1 mval2;
That tells SAS to make them available in the global area.
I have a dataset naming error_table as follows. All the variables are character
Errorno Error Resolution
001 login check
002 datacheck check
I wanted a logic that executes a sas program If the Errorno is not in 001 and 002. Else stop execution and display the error_table.
I tried the following
%macro test();
proc sql;
select trim(Error_No) into: num from error_table;
quit;
%if &num. not in ("001","002") %then %do;
%include "/path/dev/program.sas";
%end;
%else %do;
proc print data = error_table;
run;
%end;
%mend;
%test;
But, it is throwing an error.
Can anyone please correct the logic.
You need to watch out for the case when the SELECT returns zero rows. You should set a default value to the macro variable NUM.
Is your dataset variable numeric or character? Use the TRIMMED or SEPARATED BY clause instead of the TRIM() function to prevent spaces in the macro variable that is generated by the INTO clause.
%let num=NONE;
select Error_No into: num trimmed from error_table;
Remember that to the macro processor everything is a string so don't but quotes around the values you are trying to match unless they are actually part of the value.
%if NOT (&num. in (001,002)) %then %do;
Also to use the IN operator in macro code you need to make sure you have set the MINDELIMITER option.
I would sugest moving condition with error codes to proc sql.
proc sql;
select count(*) into :num_errors
from error_table
where Errorno in ("001", "002");
quit;
Then in macrovariable you have number of errors that are 001 or 002.
Next step is to check macro-condition:
%if &num_errors. > 0 %then %do;
%include "/path/dev/program.sas";
%end;
%else %do;
proc print data = error_table;
run;
%end;
%mend;
I have several .csv files in a folder that I would like to import into SAS. However, they are not always populated with data, so when I attempt to import an empty file into SAS, I get an error. I am wondering if there is some way for me to check whether or not an external file is empty, and if it is not, bring it into SAS. This is a code that I would like to automate and not have to manually check and account for blank files every time.
I came across this macro in SAS's knowledge base but am unsure of how to modify it so that it would import a .csv file, if I should be using it at all: http://support.sas.com/kb/25/072.html
I appreciate your help.
This is the macro in question from the link:
%macro test(outf);
%let filrf=myfile;
%if %sysfunc(fileexist(&outf)) %then %do;
%let rc=%sysfunc(filename(filrf,&outf));
%let fid=%sysfunc(fopen(&filrf));
%if &fid > 0 %then %do;
%let rc=%sysfunc(fread(&fid));
%let rc=%sysfunc(fget(&fid,mystring));
%if &rc = 0 %then %put &mystring;
%else %put file is empty;
%let rc=%sysfunc(fclose(&fid));
%end;
%let rc=%sysfunc(filename(filrf));
%end;
%else %put file does not exist;
%mend test;
%test(c:\test.txt)
What you'd want to do is change what the macro does in the case that the file is empty. In this case, the fget will return a 0 to the return code (%let rc = ... fget) if it is able to get a string, or it will fail and return a nonzero code.
So, just modify the %if / %else. Instead of putting something, you just do something like
%if &rc=0 %then %do;
%import_csv(&outf.);
%end;
%else %do;
%put File &outf. is empty.;
%end;
That assumes you have a macro that does your import. You could, of course, include the full PROC IMPORT code there instead.
This really makes a reasonable error checking wrapper to an import macro, in fact.
%macro import_csv(outf=,outds=);
%let filrf=myfile;
%if %sysfunc(fileexist(&outf)) %then %do;
%let rc=%sysfunc(filename(filrf,&outf));
%let fid=%sysfunc(fopen(&filrf));
%if &fid > 0 %then %do;
%let rc=%sysfunc(fread(&fid));
%let rc=%sysfunc(fget(&fid,mystring));
%let rc_close=%sysfunc(fclose(&fid));
%if &rc = 0 %then %do;
proc import file="&outf." out="&outds."
dbms=csv replace;
run;
%end;
%else %put File &outf. is empty and not imported.;
%end;
%let rc=%sysfunc(filename(filrf));
%end;
%else %put file does not exist;
%mend test;