I have tried a few ways to set up these macros. Bascially for each year there are 10 different versions of the macro, and I need to create a way to use the let statement at top to run only a single macro based on those conditions.
*What I have now:
%Let year=2001;
%Let Version=9;
%IF &YEAR=2000 and &Version=1 %THEN %DO;
%INCLUDE "macro location";
%ELSE %IF &YEAR=2000 and &Version=2 %THEN %DO;
%INCLUDE "macro location";
.....goes on until I get all of the macros included.
When I run this code it is running all of the Macros and I need it to choose the correct macro to run based on the conditions.
When used within the macro facility, your logic should work as expected.
When used in the DATA step, the %INCLUDE statement cannot be used in
conditional logic. However, you can use the %INCLUDE statement with
conditional logic when used with the macro facility. For example, you
can specify the following %IF-%THEN macro statement:
Source
However, there are alternative ways you can achieve the same goal.
If these macros are all compiled or stored in a single sasautos location, then you can call them directly and dynamically compile based off of the values of year and version:
options sasautos=(sasautos '/mylocation1' '/mylocation2' ...);
%if(&year = 2000 AND &version = 1) %then %do;
%macro1;
%end;
%else %if ...
Another alternative method would be to use call execute to conditionally execute with a data _null_ step:
data _null_;
select;
when(&year. = 2000 AND &version. = 1) call execute('%include /mylocation/macro1.sas');
when(&year. = 2000 AND &version. = 2) call execute('%include /mylocation2/macro2.sas');
....
otherwise;
end;
run;
Instead placing the %include within a conditional block, consider naming all the locations at the top and include the one that matches the parameters.
Example:
This makes the versions and locations readily available for knowing without scanning lots of code. Requires a consistent macro variable naming scheme.
%let macro_2000_v1 = /mylocation/macro1.sas ;
%let macro_2000_v2 = /mylocation/macro2.sas ;
%let macro_2000_v3 = /mylocation/macro3.sas ;
%let macro_2000_v4 = /mylocation/macro4.sas ;
%let macro_2000_v5 = /mylocation/macro5.sas ;
%let macro_2001_v6 = /mylocation/macro6_ext.sas ;
%let macro_2001_v7 = /mylocation/macro7.sas ;
%let macro_2001_v8 = /mylocation/macro8.sas ;
%let macro_2001_v9 = /mylocation/macro9.sas ;
%let macro_2001_v10 = /mylocation/macro_10_validated.sas ;
%Let year=2001;
%let version=9;
%include "&¯o_&year._v&version";
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.
so I have a code like below
%let THIS_YEAR=2020;
%macro programall;
%do i = 2016 %to &THIS_YEAR;
%let num2 =%eval(&i-2000);
%let xxx= CAT("MP",&num2);
data t_&i.;
set table1;
where GROUP in ("&xxx");
run;
%end;
for example
when i=2016
num2 = 2016-2000;
num2 = 16;
and try to concatenate with "MP", so it should create xxx=MP16.
and try to use in where statement.
but it is causing error.
how can I create Macro Variable like "MP16"correctly, so I can use it in where clause?
Thanks
You cannot use functions in macro code, they are just treated as any other text to the macro processor. But there is no need to use a function to concatenate text in macro code. Just expand the macro variable where you want to use the text it contains.
%let xxx= MP&num2 ;
Macro variables are just text (not strings, text as in the thing you type in). So to concatenate macro variables, just put them next to each other.
%let var1=Banana;
%let var2=Pepper;
%let var3=&var1. &var2.;
%put &=var3;
You don't actually have to use the third variable of course, you could just use "&var1. &var2." or whatever in your code directly.
Try
%let THIS_YEAR=2020;
%macro programall;
%local year;
%do year = 2016 %to &THIS_YEAR;
data t_&year.;
set table1;
where GROUP in ("MP%eval(&year-2000)");
run;
%end;
%mend;
options mprint;
%programall
I am getting a generic 'Statement not valid or out of order' message with the below:
%macro test;
data _null_;
%if %sysfunc(fileexist("C:\report_201809.xlsx")) = 1 %then %do;
rc=%sysfunc(rename("C:\report_201809.xlsx",
"C:\report_201809.xlsx"_old.xlsx",'file'));
%end;
%mend;
%test;
The code below should get you what you need. While you can use %if statements in a data step you generally won't need to. I'm guessing the error is coming from the %sysfunc function around the fileexist and rename functions. %sysfunc allows you to call data step functions outside of a data step so it is not needed here.
%macro test;
data _null_;
if fileexist("C:\file.txt") then do;
rc = rename("C:\file.txt", "C:\file2.txt", 'file');
end;
run;
%mend;
Alternatively, you could use an X Command that allows you to execute Windows commands. You could replace the rename function with the following statement.
x move C:\file.txt C:\file2.txt;
Remove the DATA _NULL_ or proceed per #J_Lard.
Macro arguments used in %sysfunc invoked function calls are implicitly quoted and do not need additional ' or "
%macro test;
%local rc;
%if %sysfunc(fileexist(C:\report_201809.xlsx)) = 1 %then %do;
%let rc = %sysfunc(rename(C:\report_201809.xlsx,C:\report_201809_old.xlsx,file));
%end;
%test;
You original code may have worked (by way of non-obvious side effect) if the filename "C:\report_201809.xlsx"_old.xlsx" (having an extraneous ") was corrected to "C:\report_201809_old.xlsx"
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.
Could someone please explain why it is happening?
I'm trying to find objects dependencies tree in a DB.
Let's say view5 is a view sits on top view4 which sits on top view1.
Also,
view3 sits on top view2 sits on top view1.
So,
the when I query the macro for view1, I should get back view4, view5, view2 and view3.
This is the macro:
%macro dependencies(obj=);
%let dependent_objectname =;
proc sql noprint;
select "'"||trim(dependent_objectname)||"'"
into :dependent_objectname separated by ", "
from &_input.
where src_objectname in (&obj.);
quit;
%put &dependent_objectname.;
%let dependent_objectname = (&dependent_objectname.);
%put &dependent_objectname.;
%if %length("&dependent_objectname")>0 %then
%dependencies(obj = &dependent_objectname.);
%mend dependencies;
%let source = 'ditemp.depend_test1';
%put &source.;
%dependencies(obj = &source.);
First iteration works well,
I get the objects sit on top depend_test1
in a form of "('ditemp.depend_test2','ditemp.depend_test3')"
then I'm checking for the length of variable dependent_objectname (greater than zero)
and calling the macro again,
only it never stops...
I see a couple problems.
The statement:
%if %length("&dependent_objectname")>0 %then %do;
will always return true, even if the value of &dependent_objectname is null. Because the quotes are part of the value in the macro language. You probably want:
%if %length(&dependent_objectname)>0 %then %do;
That test for nullness usually works. Or see this paper for better methods. http://support.sas.com/resources/papers/proceedings09/022-2009.pdf
Before that, the statement:
%let dependent_objectname = (&dependent_objectname.);
is adding parentheses to your value. So again, even if &dependent_objectname were null, it would be () after this. It looks like you don't need these parentheses, so I would skip this statement.
I would also add:
%local dependent_objectname ;
to the top of the macro. That way each invocation of the macro will have its own local macro variable, rather than having them all use the macro variable created in the first iteration (or worse yet, all use a global macro variable).
You have sensibly added %PUT statements to help with debugging. I would expect they would show that the value of &dependent_objectname is always non-null as currently written. You could also add:
%put The length is: %length(&dependent_objectname.) ;
Since you are using an SQL query to generate the dependent list you can use the automatic variable SQLOBS in your test to break the recursion.
%if &sqlobs %then %do;
%dependencies(obj = &dependent_objectname.);
%end;
Also do NOT use a comma as the delimiter between the items listed in the OBJ parameter. The IN operator in SAS doesn't need them and they will cause trouble in the macro call.
select * from sashelp.class where name in ('Alfred' 'Alice') ;
So your macro could look like this:
%macro dependencies(object_list);
%local dependent_list ;
proc sql noprint;
select catq('1as',dependent_objectname)
into :dependent_list separated by ' '
from &_input.
where src_objectname in (&object_list)
and dependent_objectname is not null
;
quit;
%put Dependent Objects of (&object_list) = (&dependent_list);
%if &sqlobs %then %dependencies(&dependent_list);
%mend dependencies;
And here is a test case.
%let _input=sample;
data sample;
length src_objectname dependent_objectname $41 ;
input (_all_) (:) ;
cards;
object1 object2
object2 object3
object2 object4
;;;;
%dependencies('object1');