I am setting up a batch script in SAS, I want it to run monthly and output to table, the code works when running manually but I am having difficulty setting up the macros.
I have created 3 macros, one for the previous month (please ignore the hard coded 12 and the commented out code, I was just testing if it worked or not, the commented out code will remain and the hard coded 12 will be removed when I can get it working), one for the current year and one for the previous year. The issue I will have is that the script grabs month end data, so come January the year will be set to 2020 but the month will be looking for December data.
`%let pmonth = 12 /*%sysfunc(month(%sysfunc(intnx(month,"&sysdate"d ,-1))))*/;`
`%put &pmonth;`
`%let year1 = %sysfunc(year("&sysdate"d));`
`%put &year1;`
`%let year2 = %sysfunc(year(%sysfunc(intnx(year,"&sysdate"d ,-1))));`
`%put &year2;`
`%macro year;
%if &pmonth = 12 %then %do;
&year2;
%end;
%else %do;
&year1;
%end;
%mend;
%year;`
What I would like is for the macro year, which is used in my code, to select correctly between year1 and year2 based on the month macro pmonth. I have played around a bit with the macros but if I'm honest, its not an area of SAS I've used that often.
A little terminology first. Your code has one macro defined, %YEAR. It does have three macro variables named YEAR1, YEAR2 and PMONTH. And there is no macro variable named YEAR that your code is either attempting to create or use. And your code does not have any IF statements. But there is one %IF statement inside the definition of the YEAR macro.
You probably will want to convert your macro variable values into actual DATE values to deal with the year issue. So if your input is two macro variables with YEAR and MONTH you can convert that into a date.
%let year1=2018;
%let month=1;
%let date=%sysfunc(mdy(&month,1,&year1));
Then if you want to find the end of the previous month you can use the date in the INTNX() function call.
%let end_of_prev_month=%sysfunc(intnx(month,&date,-1,e));
Then if you need to you can get the YEAR and MONTH for that new date.
%let year2=%sysfunc(year(&end_of_prev_month));
%let month2=%sysfunc(month(&end_of_prev_month));
Related
I would like to use a macro in SAS to calculate the last day of the current month when executed.
As i'm quite new to the SAS macro's i've tried to create on based on the information i've found on the internet.
%let last_day = %sysfunc(putn(%sysfunc(intnx(month,%sysfunc(today()),e), date9.));
However it does not seem to work when i execute it.
You left out the number of intervals in the INTNX() function call.
To create a macro variable with the string that looks like the last day of the current month in the style produced by the DATE9. format just use:
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e), date9.);
You could then use that macro variable to generate strings. Such as in a TITLE statement.
TITLE "End of the month is &last_day";
If you want to use it as an actual date you will need to convert it to a date literal by adding quotes and the letter d.
...
where date <= "&last_day"d ;
And if so it is probably simpler to not use the DATE9. format at all and just store the raw number of days since 1960 in the macro variable.
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e));
...
where date <= &last_day ;
I am writing a conditional in SAS starts with DATA NULL
%LET today = today();
DATA _NULL_;
if day(today) ge 1 and day(today) le 15 then do;
date1=put(intnx('month',today,-1,'E'), date11.);
date2=put(intnx('month',today,-1,'L'), date11.);
end;
if day(today) > 15 then do;
date1=put(intnx('month',today,0,'B'), date11.);
date2=put(intnx('month',today,0,'L'), date11.);
end;
call symput('report_date',date1);
call symput('report_date2',date2);
RUN;
but with above, I am not getting any values for my report_dates.
the condition is:
date 1 = If the current date is greater than or equal to 1 and less than 16, set the date1 to the 16th of the previous month, otherwise set it to the 1st of the current month
date2 = If the current date is 16 and above, set the date2 to the 15th of the current month, otherwise set date2 to the last day of the previous month
The IF/THEN logic does not account for the missing value you passing to the DAY() function calls.
The variable TODAY is never created in the data step. So just remove the %LET statement and add an actual assignment statement instead.
DATA _NULL_;
today=today();
if day(today) ge 1 and day(today) le 15 then do;
...
Just because you used the same name for the macro variable in the %LET statement as you used for the variable in the data step does not imply that the two have anything at all to do with each other.
If you wanted to use the macro variable to generate the code for the data step you would need to replace the TODAY with &TODAY.
if day(&today) ge 1 and day(&today) le 15 then do;
So for the value you set to the macro variable TODAY it would mean that the SAS code you are trying to run is:
if day(today()) ge 1 and day(today()) le 15 then do;
Note that is actually not a good way to handle this problem because it is calling the TODAY() function multiple times. That could cause strange results if the data step started right before midnight. So the IF condition might be run on the 31st but when you get to the ELSE condition the clock has ticked over to the first of the next month.
Relatively new to SAS and looking for some help with using date functions.
I'm using Today()-x to set dates and making it available for looking at historic data (a rare need, but worth including) and have tried writing an IF statement around it, as I need a slight change for financial year end.
My code is as follows:
%macro RunDate_Variables;
%global rep_month repdt prevdt;
data _null_;
rundate = today()-0;
runmonth = Month(rundate);
%if runmonth eq 04
%then %do;
ReportDate = MDY(4, 4, Year(rundate);
PrevReportDate = intnx('Month', ReportDate,-2,'e');
%end;
%else %if runmonth eq 05;
%then %do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = iMDY(4, 4, Year(rundate));
%end;
%else %do;
ReportDate = intnx('Month', rundate,-1,'e');
ReportDate = intnx('Month', ReportDate,-1,'e');
%end;
call symputx('rep_month', put(ReportDate, MONYY7.));
call symputx('repdt', put(ReportDate, yymmddN8.));
call symputx('prevdt', put(PrevReportDate, yymmddn8.));
run;
%mend;
%RunDate_Variables;
Unfortunately, the runmonth doesn't seem to be doing what I'd hoped it would do and therefore isn't resolving as expected within the macro.
Ultimately, what I'm looking for, is for the code to identify when the code is being run in April and set the ReportingDate to 4th April (for Financial Year End) and PrevReportingDate to 28th (or 29th) Feb, and for when the code is run in May for the ReportingDate to be set as 30th April and PrevReportingDate to be set as 4th April.
The code won't need to be run in the space between 31st March and 4th April, so having it resolve to 4th April in that space won't be an issue.
Any suggestions appreciated. The code doesn't need to stick with the format I've attempted, the only bit that needs to remain the same are the variable names rep_month, repdt and prevdt as these were the names used throughout the code when I picked the project up.
EDIT: I've managed to make the idea work using a manually adjusted flag outside the data step and altering the IF conditions, but the issue for this is that it requires either two flags (one for April and one for May), or two separate inputs to be read from the Flag (again, one to recognise April, the other May). Whilst this works, I'd still like to look into the possibility of fully automating this, as FYE is an easy time to remember to alter the flag, but altering the flag in the month after FYE will likely be less memorable.
Thanks.
You cannot check the values of data variables with macro code, your %IF statement is just comparing the constant string runmonth to the constant string 04 . They will never be equal.
I don't see anything here that requires any macro logic, but you could still keep the macro definition if you want to call the same data step multiple times.
data _null_;
rundate = today()-0;
runmonth = Month(rundate);
if runmonth eq 04 then do;
ReportDate = MDY(4, 4, Year(rundate);
PrevReportDate = intnx('Month', ReportDate,-2,'e');
end;
else if runmonth eq 05 then do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = MDY(4, 4, Year(rundate));
end;
else do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = intnx('Month', ReportDate,-1,'e');
end;
call symputx('rep_month', put(ReportDate, MONYY7.),'g');
call symputx('repdt', put(ReportDate, yymmddN8.),'g');
call symputx('prevdt', put(PrevReportDate, yymmddn8.),'g');
run;
Background:
I have a code that pulls transactional data starting at the beginning of the current calendar quarter, but from an year ago.
For example, if I run the code today (August 16, 2013) it will have to pull all the data from July 1, 2012 onwards.
Problem:
I want to automate the starting date for the data pull with a macro variable.
So far, I'm stuck here:
%let ThisYear = %Sysfunc(Date(), YEAR.);
%let LastYear= %eval(&ThisYear-1); /* I get the starting year */
%let QTR_start_month= %eval(3*%Sysfunc(Date(), qtr.)-2); /* this gives me the current quarter starting month. If I run it in August, it outputs 7 for July */
%let start_date=%str(01/%Sysfunc(month(&QTR_start_month))/&lcy);
The final macro variable outputs the date which I want, but in a format which is not recognized by SAS.
I will greatly appreciate any help.
Many thanks in advance!
You can either input that date to a date format, or construct it like a SAS date literal ('01JUL2013'), DDMONYY(YY), or construct it as a date value directly.
INTNX is probably your best option here to construct it; you don't need all that work.
%let start_date = %sysfunc(intnx(Quarter,%sysfunc(date()),-4),DATE9.);
%put &start_date;
You can leave DATE9. to use it as a date literal, or remove the ,DATE9. to get the numeric value that can be used directly. You would use this as "&start_Date."d to use the date literal.
This should do the job.
data test;
format todays_date starting_qtr date9.;
todays_date=today();
/*this takes today's date and rolls back 4 qtrs and sets that date to the first day of that quarter*/
starting_qtr = intnx('qtr',todays_date,-4,'b');
/*so, running this code today, 16AUG2013 would yield starting_qtr=01JUL2012 */
call symputx('start_date', put(starting_qtr, date9.));
run;
%put &start_date.;
UPDATE I've been told this isn't possible using arrays because of they way they are stored. This changes my question a bit, but the gist is still the same. How can I most efficiently generate the tables I need from a given vector of values (ex: day, week, month, year) without just repeating the code multiple times? Is there any way to simply substitute the given date value into INTX in a loop?
Ok, this is my last question on this subject, I promise. After some good advice, I'm using the INTX function. However, I'd like to just loop through the different categories I select and create tables. I tried this, but to no avail.
data;
array period [*] $ day week month year;
run;
%MACRO sqlloop;
proc sql;
%DO k = 1 %TO dim(&period); /* in case i decide to drop/add from array later */
%LET bucket = &period[&k];
CREATE TABLE output.t_&bucket AS (
SELECT INTX( "&bucket.", date_field, O, 'E') AS test FROM table);
%END
quit;
%MEND
%sqlloop
Sadly this doesn't work because I'm fouling up the array reference somehow. If I can get this step I'll be in good shape.
You could replace your array with a macro variable string:
%let period=day week month year;
In your macro then, you loop over the words in the macro variable:
%MACRO sqlloop;
proc sql;
%DO k = 1 %TO %sysfunc(countw(&period.)); /*fixed extra s*/
%LET bucket = %scan(&period.,&k.);
CREATE TABLE output.t_&bucket AS (
SELECT INTNX( "&bucket.", date_field, 0, 'E') AS test FROM table);
%END;
quit;
%MEND;
%sqlloop
edit you forgot some semicolons apparently. :p