BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
yichentian226
Obsidian | Level 7

Hi all!

 

I am reading some codes and wondering if anyone familiar with MACRO can help me out:

 

/* Run Parameters : */
/* Below parameters need to change before running report in Feb */
/*******************************************************************/

%LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));
%LET NextYEAR=%EVAL(&RUNYEAR.+1);
%LET PREVYEAR=%EVAL(&RUNYEAR.-1);
%LET PREV2YEAR=%EVAL(&RUNYEAR.-2);

%LET RunYearDT = 2020-01-01 00:00:00;
%LET RunYearD = 01JAN2020;
%LET ContributionStartDT = '01/01/2014' and '12/31/2020'; 
%PUT RUNYEAR = &RUNYEAR. NextYEAR = &NextYEAR. PREVYEAR = &PREVYEAR. PREV2YEAR = &PREV2YEAR. RunYearDT = &RunYearDT. ContributionStartDT = &ContributionStartDT.;

 

Here my question is, why would we need to write eg:%LET RunYearDT = 2020-01-01 00:00:00; since we already have "%LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));" in the first row? I thought this already helped us define the run year. 

And since we run January data in Feb, I should change all the "2020" to "2021" I think? But same question came out, isn't runyear defined by the first macro step? 

 

Thanks!

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

Macro variables just hold text.  You need to understand what the text means and how to use that text to generate other code to run.  First let's just run your %LET statements and see what text is being assigned to the macro variables.

Note the newish feature of the %PUT statement where when you type &=macrovar instead of &macrovar it displays the name and the value.

156  %LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));
157  %put &=runyear;
RUNYEAR=2021
158
159  %LET NextYEAR=%EVAL(&RUNYEAR.+1);
160  %put &=nextyear;
NEXTYEAR=2022
161
162  %LET PREVYEAR=%EVAL(&RUNYEAR.-1);
163  %put &=prevyear;
PREVYEAR=2020
164
165  %LET PREV2YEAR=%EVAL(&RUNYEAR.-2);
166  %put &=prev2year;
PREV2YEAR=2019
167
168
169  %LET RunYearDT = 2020-01-01 00:00:00;
170  %put &=runyeardt ;
RUNYEARDT=2020-01-01 00:00:00
171
172  %LET RunYearD = 01JAN2020;
173  %put &=runyeard ;
RUNYEARD=01JAN2020
174
175
176  %LET ContributionStartDT = '01/01/2014' and '12/31/2020';
177  %put &=ContributionStartDT ;
CONTRIBUTIONSTARTDT='01/01/2014' and '12/31/2020'

So the first one is the most complex.  First let's simplify it a little.

%LET RUNYEAR=%SYSFUNC(YEAR(%SYSFUNC(TODAY())-10));

The %SYSFUNC() macro function let's you call regular SAS functions in macro code (almost all SAS functions can be called this way).   In this case YEAR() and TODAY() are the functions being called, notice how each one needs its own %SYSFUNC() wrapper.  The YEAR() function returns the year from a SAS date value.  The TODAY() function returns the date value from the computers clock.  SAS stores dates as number of days so subtracting 10 is moving 10 days into the past.  Notice that when calling a SAS function in macro code via %SYSFUNC() you do not need to call %EVAL() for arithmetic you want to do on numeric arguments. The %SYSFUNC() interface to the SAS functions will make sure those expressions are evaluated.

 

Then you have two %LET statements that using %EVAL() macro function to perform some simple integer arithmetic. So in this one:

%LET NextYEAR=%EVAL(&RUNYEAR.+1);

You are passing into the %EVAL() function the string 2021+1 which it will see as a request to add the two numbers represented by the character strings on either site of the plus sign.  The resulting number will be an integer that is then converted into a digit string that represents that number. In this case the string 2022.

 

Near the end you are making two macro variables with DT and D suffixed to their name.  The strings being assigned look like the way that the DATETIME and DATE formats would display datetime and date values.  But more importantly those strings are strings that the DATETIME and DATE informats will recognize as dateime and date values. So you could use those strings to create DATETIME or DATE literals by adding quotes and the proper suffix.  For example you might use both of those macro variables in SAS code like this to compare some datetime or date variables to those constant values.

where run_datetime > "&RunYearDT"dt  or run_date > "&RunYearD"d ;

 

The last example is stranger as you are assigning a multiple word string to the macro variable.  It is probably not very useful for generating SAS code.  You might use it to generate a title or a footnote.  So code like:

title1 "Report for Contributions between &ContributionStartDT";

Would result in this actual TITLE statement:

title1 "Report for Contributions between '01/01/2014' and '12/31/2020'";

Or it might be useful in some code passed to a remote database where date constants can be expressed in that way. So perhaps something like:

proc sql ;
connect to oracle ..... ;
create table want as select * from connection to oracle
(select * from contributions where datevar between &ContributionStartDT)
;

So that the query sent to the ORACLE database is:

select * from contributions where datevar between '01/01/2014' and '12/31/2020'

But perhaps want you really want to do is use the PREVYEAR and NEXTYEAR macro variables to generate date literals that you could use in SAS code instead?  So something like this in your program:

where datevar between "01JAN&prevyear"d and "31DEC&nextyear"d 

Would result in this SAS code being run:

where datevar between "01JAN2020"d and "31DEC2022"d 

 

View solution in original post

13 REPLIES 13
tarheel13
Rhodochrosite | Level 12

You are using the year function in runyear so that just gets the year so if today is 1/27/2021, the year function would return 2021. Runyeardt is a whole date variable, not just the year.

PaigeMiller
Diamond | Level 26

You can figure out many of these questions by simply looking at the values of the macro variables.

 

%LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));
%put &=runyear;

So your can see in the LOG that &RUNYEAR has a value of 2021.

 

Why do you need &RUNYEARDT? This is a text string containing a datetime value. At some point, you may need to work with datetime values, so &RUNYEAR doesn't get it done.


Whether or not you need to change the 2020 in the data to 2021 is up to you, we don't know what the rest of the code is doing.


Also, to clear up the terminology, your title implies you have a SAS macro question, but you do not. You have a question about macro variables. Macro variables are not macros, and macros are not macro variables, so it will be helpful to you (and helpful to us) not to confuse the two.

--
Paige Miller
yichentian226
Obsidian | Level 7
Hi, thanks for your reply. Sorry for the confusion on the title, I was reading other ppl's codes and am not familiar with Macro, so I simply wrote "macro question".
S
tarheel13
Rhodochrosite | Level 12
%let is assigning new macro variables.
yichentian226
Obsidian | Level 7
Hi lrackley, thank you! Can you pls help me understand what "%LET RunYearDT = 2021-01-01 00:00:00" will do? Sorry that I don't know much about macro. I understand you said that %let assigns new variables tho...
tarheel13
Rhodochrosite | Level 12

Macros are basically like text substitution. That statement is making a new macro variable called RunYearDt and assigning it to 2021-01-01 00:00:00. You can reference macro variables in other parts of the program by preceding them with &. 

 

https://support.sas.com/resources/papers/proceedings/proceedings/sugi26/p153-26.pdf

 

Here's a paper where you can read more. SAS e-learning also has 2 courses SAS Macro 1 and SAS Macro 2 that you could study them. They are a powerful tool for automating repetitive tasks.

yichentian226
Obsidian | Level 7
Hi, thanks for the answer. Sorry about the confusion on the title as I wasn't and still am not familiar with macro.
Actually I was reading other people's codes, you're right runyeardt will be used later, but maybe I wasn't understanding the logic right, if we say "%LET RunYearDT = 2021-01-01 00:00:00", are we formating runyeardt? I am trying to learn what will this piece of code help us achieve/ what it does. I was thinking we should change the 2021 because we will only looking at 2021/01's numbers... just my thought haha
tarheel13
Rhodochrosite | Level 12

Did you try running the code and looking at the log? 


260 /* Run Parameters : */
261 /* Below parameters need to change before running report in Feb */
262 /*******************************************************************/
263
264 %LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));
265 %LET NextYEAR=%EVAL(&RUNYEAR.+1);
266 %LET PREVYEAR=%EVAL(&RUNYEAR.-1);
267 %LET PREV2YEAR=%EVAL(&RUNYEAR.-2);
268
269 %LET RunYearDT = 2020-01-01 00:00:00;
270 %LET RunYearD = 01JAN2020;
271 %LET ContributionStartDT = '01/01/2014' and '12/31/2020';
272 %PUT RUNYEAR = &RUNYEAR. NextYEAR = &NextYEAR. PREVYEAR = &PREVYEAR. PREV2YEAR = &PREV2YEAR. RunYearDT = &RunYearDT.272! ContributionStartDT = &ContributionStartDT.;
RUNYEAR = 2021 NextYEAR = 2022 PREVYEAR = 2020 PREV2YEAR = 2019 RunYearDT = 2020-01-01 00:00:00 ContributionStartDT = '01/01/2014'
and '12/31/2020'

 

This is what's in the log. I am assuming the DT means datetime. So that is in datetime format. Are you running some kind of routine report? 

yichentian226
Obsidian | Level 7
Hi! Thanks for sharing the macro learning document this is super helpful!

To answer your question, yes i am running a routine report, i am not the product owner that's why i am trying to understand the logic behind. I think the reason the owner commented "change below parameters in Feb" is because in Feb we run report for Jan, so that all the 2020 should be changed to 2021 that's what I thought... The runyeardt was kept as %LET RunYearDT = 2020-01-01 00:00:00; in the whole last year, so part of me not understanding macro was asking " then what about feb, march, april's report if the runyeardt=2020-1-1". And I ran the code, i got the same log 🙂
tarheel13
Rhodochrosite | Level 12
Okay, yes you would need to change those macro variables if it’s supposed to be for 2021. The %put statement allows you to print what the macro variables resolve to in the SAS log.
PaigeMiller
Diamond | Level 26

@yichentian226 wrote:
if we say "%LET RunYearDT = 2021-01-01 00:00:00", are we formating runyeardt?

No, you are create a macro variable RunYearDT that contains a value which is a text string 2021-01-01 00:00:00. There has been no formatting.

 

I am trying to learn what will this piece of code help us achieve/ what it does. I was thinking we should change the 2021 because we will only looking at 2021/01's numbers... just my thought haha

The best way to learn what the code is doing is to run it, and include debugging as I mentioned earlier, printing out values of the macro variables using the %PUT statement.

 

--
Paige Miller
Tom
Super User Tom
Super User

Macro variables just hold text.  You need to understand what the text means and how to use that text to generate other code to run.  First let's just run your %LET statements and see what text is being assigned to the macro variables.

Note the newish feature of the %PUT statement where when you type &=macrovar instead of &macrovar it displays the name and the value.

156  %LET RUNYEAR=%SYSFUNC(YEAR(%EVAL(%SYSFUNC(TODAY())-10)));
157  %put &=runyear;
RUNYEAR=2021
158
159  %LET NextYEAR=%EVAL(&RUNYEAR.+1);
160  %put &=nextyear;
NEXTYEAR=2022
161
162  %LET PREVYEAR=%EVAL(&RUNYEAR.-1);
163  %put &=prevyear;
PREVYEAR=2020
164
165  %LET PREV2YEAR=%EVAL(&RUNYEAR.-2);
166  %put &=prev2year;
PREV2YEAR=2019
167
168
169  %LET RunYearDT = 2020-01-01 00:00:00;
170  %put &=runyeardt ;
RUNYEARDT=2020-01-01 00:00:00
171
172  %LET RunYearD = 01JAN2020;
173  %put &=runyeard ;
RUNYEARD=01JAN2020
174
175
176  %LET ContributionStartDT = '01/01/2014' and '12/31/2020';
177  %put &=ContributionStartDT ;
CONTRIBUTIONSTARTDT='01/01/2014' and '12/31/2020'

So the first one is the most complex.  First let's simplify it a little.

%LET RUNYEAR=%SYSFUNC(YEAR(%SYSFUNC(TODAY())-10));

The %SYSFUNC() macro function let's you call regular SAS functions in macro code (almost all SAS functions can be called this way).   In this case YEAR() and TODAY() are the functions being called, notice how each one needs its own %SYSFUNC() wrapper.  The YEAR() function returns the year from a SAS date value.  The TODAY() function returns the date value from the computers clock.  SAS stores dates as number of days so subtracting 10 is moving 10 days into the past.  Notice that when calling a SAS function in macro code via %SYSFUNC() you do not need to call %EVAL() for arithmetic you want to do on numeric arguments. The %SYSFUNC() interface to the SAS functions will make sure those expressions are evaluated.

 

Then you have two %LET statements that using %EVAL() macro function to perform some simple integer arithmetic. So in this one:

%LET NextYEAR=%EVAL(&RUNYEAR.+1);

You are passing into the %EVAL() function the string 2021+1 which it will see as a request to add the two numbers represented by the character strings on either site of the plus sign.  The resulting number will be an integer that is then converted into a digit string that represents that number. In this case the string 2022.

 

Near the end you are making two macro variables with DT and D suffixed to their name.  The strings being assigned look like the way that the DATETIME and DATE formats would display datetime and date values.  But more importantly those strings are strings that the DATETIME and DATE informats will recognize as dateime and date values. So you could use those strings to create DATETIME or DATE literals by adding quotes and the proper suffix.  For example you might use both of those macro variables in SAS code like this to compare some datetime or date variables to those constant values.

where run_datetime > "&RunYearDT"dt  or run_date > "&RunYearD"d ;

 

The last example is stranger as you are assigning a multiple word string to the macro variable.  It is probably not very useful for generating SAS code.  You might use it to generate a title or a footnote.  So code like:

title1 "Report for Contributions between &ContributionStartDT";

Would result in this actual TITLE statement:

title1 "Report for Contributions between '01/01/2014' and '12/31/2020'";

Or it might be useful in some code passed to a remote database where date constants can be expressed in that way. So perhaps something like:

proc sql ;
connect to oracle ..... ;
create table want as select * from connection to oracle
(select * from contributions where datevar between &ContributionStartDT)
;

So that the query sent to the ORACLE database is:

select * from contributions where datevar between '01/01/2014' and '12/31/2020'

But perhaps want you really want to do is use the PREVYEAR and NEXTYEAR macro variables to generate date literals that you could use in SAS code instead?  So something like this in your program:

where datevar between "01JAN&prevyear"d and "31DEC&nextyear"d 

Would result in this SAS code being run:

where datevar between "01JAN2020"d and "31DEC2022"d 

 

yichentian226
Obsidian | Level 7
This is EXTREMLY helpful! Thanks for the detailed explanation I learned from you 🙂

SAS Innovate 2025: Register Today!

 

Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.


Register now!

How to Concatenate Values

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 13 replies
  • 2257 views
  • 2 likes
  • 4 in conversation