I am calculating around 12 metrics (Say Sales for each month individually for latest 12 months). Every month I need to go manually and change the month everywhere. If there is any way to automate it, it would be very helpful. My code is
proc sql;
create table inter.calls as
select a.district_name,
sum(01JAN2016,01FEB2016,01MAR2016)/terr_count as q1_workingdays,
sum(01APR2016,01JUN2016,01MAY2016)/terr_count as q2_workingdays,
sum(01AUG2016,01SEP2016,01JUL2016)/terr_count as q3_workingdays,
sum(01NOV2016,01DEC2016,01OCT2016)/terr_count as q4_workingdays
from inter.calls_made_bymon_reg3 a left join inter.territory_count b
on a.district_name=b.district_name;
quit;
Now when I refresh for JAN2017, I need to change from FEB2016 to JAN2017 for latest 12months. Every time it is difficult to change the code manually.
I will be very thankful if I get any help!!
It's difficult to understand your problem completely, because as Reeza mentioned the arguments in your sum() functions are invalid (the names begin with a number).
However, I do understand the desire not to manually change dates all over the place. You might find a macro like the below helpful:
%macro prev_month(n);
%let latest_date = %sysfunc(intnx(month,%sysfunc(inputn(&latest_month.,monyy7.)),-&n.));
days_%sysfunc(putn(&latest_date.,monyy7.))
%mend prev_month;
And you would then use it in your query like so:
%let latest_month = JAN2017;
proc sql;
create table inter.calls as
select a.district_name,
sum(%prev_month(11),%prev_month(10),%prev_month(9))/terr_count as q1_workingdays,
sum(%prev_month(8) ,%prev_month(7) ,%prev_month(6))/terr_count as q2_workingdays,
sum(%prev_month(5) ,%prev_month(4) ,%prev_month(3))/terr_count as q3_workingdays,
sum(%prev_month(2) ,%prev_month(1) ,%prev_month(0))/terr_count as q4_workingdays
from inter.calls_made_bymon_reg3 a left join inter.territory_count b
on a.district_name=b.district_name;
quit;
Hope it helps.
Related
I am new to SAS and I have the following problem:
When trying to join records I just imported (in one table) with records I have stored in another table.
What happens is that I am going to run the code in SAS daily, and I need the table that I am going to create today (17/05/2021) by importing a file 'X', to join the table that I created yesterday (16/05/2021) by importing a file 'Y'.
And so the code will be executed tomorrow, the next day and so on.
In conclusion the records will accumulate as the days go by.
To tackle this problem, I am first creating two variables, one with the date of the day the code will be executed and the other with the date of the last execution.
%let daily_date = 20210423; /*AAAAMMDD*/
%let last_execution_date = 20210422; /*AAAAMMDD*/
Then the import of a file is done, we can see that the name of this created table has the date of the day in which the code is being executed.
data InputAC.RA_ratings&daily_date;
infile "&ruta_InputRA." FIRSTOBS=2
dsd lrecl=4096 truncover;
input
#1 RA_Customer_ID $10.
#11 Rating_ID 10.
#21 ISRM_Model_Overlay_ID $10.
#31 Constant_ID 10.
#41 Value $100.
;
run;
proc sort data=inputac.RA_ratings&daily_date;
by RA_Customer_ID Rating_ID;
quit;
Finally the union of InputAC.RA_ratings&daily_date with InputAC.RA_ratings&last_execution_date is made. ('InputAC.RA_ratings&last_execution_date' should be the table that was imported at an earlier date than today.)
data InputAC.RA_ratings&fec_diario;
merge
InputAC.RA_ratings&fec_diario
InputAC.RA_ratings&ultima_fecha_de_ejecucion;
by RA_Customer_ID Rating_ID;
run;
This is how the tables are being stored on the server.
(Ignore date 20210413, let's imagine it is 20210422)
However, I have to perform this task without using the variable 'last_execution_date'.
I've been researching but I still can't find any SAS function that can help me with this problem.
I hope someone can help me, thank you very much in advance.
This is a pretty complex and interesting question from an operations point of view. The answer depends on a few things.
How much control do you have over the execution of this process?
Is "yesterday" guaranteed, or does the process need to work if "last execution date" is not yesterday?
What should happen if the process is run twice today?
The best practices way to solve this is to have a dataset (or table) that stores the last execution date. That allows you to handle #2 trivially, and the answer to #3 might guide exactly how you store this but is easily handled anyway.
Say for example you have a table, MetaAC.LastExecDate (or, in spanish, MetaAC.UltimaFecha or similar). It could store things this way:
data LastExecDate;
timestamp = datetime();
execdate = input(&daily_date,yymmdd8.);
run;
proc append base=MetaAC.LastExecDate data=LastExecDate;
run;
This lets you store an arbitrary execdate even if it's not today, and also store when you ran it (for audit purposes), and you could even add who ran it if that's interesting (there is a macro variable &sysuserid or similar). Then put all this at the bottom of your process, and it updates as you go.
Then, you can pull out from this the exact info you want - for example,
proc sql;
select max(execdate)
into :last_exec_date
from MetaAC.LastExecDate
where execdate ne today()
;
quit;
Now, if you don't have control over this for some reason, you could determine this in a different way. Again, the exact process depends on your circumstances and your answers to 2 and 3.
If your answer to 2 is you always want it to be yesterday, then this is really easy - just do this:
%let daily_date=20210517;
%let last_execution_date = %sysfunc(putn(%sysevalf(%sysfunc(inputn(&daily_date,yymmdd8.))-1),yymmddn8.));
%put &=last_execution_date;
The two %sysfuncs just do the input/put from SAS datastep inside the macro language, and %sysevalf lets you do math.
If you don't want it to always be the prior day (if there are weekends, or other days you don't necessarily want to assume it's the prior day), then your best bet is to either use the dictionary tables to look at what's there and find the largest date prior to your date, or maybe use a x command to look at the folder and do the same thing (might be easier to use OS command than to use SQL for this, sometimes SQL dictionary tables can be slow).
hi I just wanted to know what would be the code in a data step for this outcome
I have the following:
and need the below:
please direct me to another page if this was answered previously...in addition how would I asked something like this in the future.
Thanks
Do you really need to do this in a data step? You might as well use proc sql for a Cartesian product.
proc sql;
create table want as
select code, groupds
from (select distinct code from have) a,
(select distinct groupds from have) b;
quit;
Here is how you can do it in a data step.
data want;
set have(keep=code where=(not missing(code)));
do i=1 to n;
set have(keep=groupds where=(not missing(groupds))) point=i nobs=n;
output;
end;
run;
The issue with this method is if you have a duplicate code or groupds a record will be created for the duplicate entry.
I'm trying to have a SAS data set automatically limit the results based on date but don't want to manually have to manually change the date through a %Let statement.
If I try
%let BeginDate = %EVAL(MDY(MONTH(TODAY()), 1, YEAR(TODAY()));
I get a "Open code statement recursion detected"... I've tried &SYSFUNC and &SYSEVALF but no luck either. It seems like this should be much simpler... any suggestions would surely be appreciated.
Thanks!
#Joe's method is the most straightforward. Additionally, if you wanted to do this in a datastep with similar syntax you could do:
data _null_;
call symputx('BeginDate_ds',mdy(month(today()),1,year(today())));
run;
%put &BeginDate_ds.;
Depending on what you're doing, you either don't need anything, or you need %SYSFUNC.
If you want to have &begindate evaluate to an actual date value, you would use %SYSFUNC.
However, you have five functions there - that's going to require a bunch of sysfuncs, though I think we can do two not five.
%let begindate = %sysfunc(intnx(MONTH,%sysfunc(today()),0,b));
%put &begindate;
We use INTNX with the MONTH and B(eginning) options to tell SAS to go ahead 0 months (so current month) and to go to the Beginning of that month. A second SYSFUNC grabs TODAY(). You could simplify this more:
%let begindate = %sysfunc(intnx(MONTH,"&sysdate."d,0,b));
%put &begindate;
&SYSDATE is a macro variable that stores the system date when SAS was started up; so only use that if you're okay with that (i.e., if SAS likely/definitely started up today).
With SYSFUNC don't forget that you need to drop quotation marks, with the one big exception of the date constant above - that is okay to use them - but note "MONTH" and "b" are not quoted.
I have the following problem. We have several streams in Enterprise Miner and we would like to be able to tell how long was each run. I have tried to create a macro that would save the start and end time/date but the problem is that global variables defined in a node, are not seen anymore in a subsequent node (so are global only inside a node, but not between nodes). How people usually solve the problem? Any idea or suggestion?
Thanks, Umberto
Just write out timestamps to log (EM should produce a global log in the same fashion that EG and DI do)
Either use:
data _null_;
datetime = datetime();
put datetime= datetime20.;
run;
or macro language:
%put EM node started at at %sysfunc(time(),timeampm.) on %sysfunc(date(),worddate.).;
with a higly customised message you have read the log in SAS looking for those strings using regex.
Solution 2:
Other option is to created a table in a library that is visible from EM and EG for example and have sql inserts at the beginning/end of your process.
proc sql;
create table EM_logger
(jobcode char(100),
timestamp num informat=datetime20. format=datetime20.);
quit;
proc sql;
insert into EM_logger values('Begining Linear Reg',%sysfunc(datetime()));
quit;
data w;
do i=1 to 10000000;
output;
end;
run;
proc sql;
insert into EM_logger values('End Linear Reg',%sysfunc(datetime()));
quit;
Table layout can be as complex as you want and as long as you can access it you can get your statistics.
Hope it helps
I have a sas data set. In it i have some variables following a pattern
-W 51 Sales
-W 52 Sales
-W 53 Sales
and so on.
Now i want to rename all of these variables dynamically such that W 51 is replaced by starting date of that week and the new name becomes - 5/2/2013 Sales?
The reason i want to rename them is that i have sales data of all the 53 weeks in an year and the data set would be eassier for me to understand if i had the starting date of a week instead of W(week_no) Sales as a variable name
Is there any way i can do that in sas?
You really don't want to rename your variables. You may think you do, but it'll just bite you eventually.
What you can do instead is give them descriptive labels. This can be done via proc datasets.
proc datasets library=<lib>;
modify <dataset>;
label <variable> '5/2/2013 sales';
run;
Just for fun lets assume you want to do this anyway -- Safest thing to do is just create a copy of the dataset for your output...
this code assumes your variable names are named like w1_sales and output names are going to be renamed to 03JAN2013_sale or something like that.
data newDataSet;
set oldDataSet;
%MACRO rename_vars(mdataset,year);
data &mdataset.;
set &mdataset.;
%do i = 1 %to 53;
%let weekStartDate = %sysfunc(intnx('week&i','01jan&year.'d,0)); %*returns the starting day of week(i) uses sunday as starting date. If you want monday use 0.1 as last param;
%let weekstartDateFormatted = %sysfunc(putn(&weekStartDate.,DATE.)) %*formats into ddMONyyy. substitute whatever format you want;
rename w&i._Sale = &weekstartDateFormatted ._SALES;
%end;
run;
%MEND rename_vars;
%rename_vars(newDataSet,2013);
I don't have time to test this right now, so sommebody let me know if I screwed it up somewhere. This should at least get you going though. Or you can send me or post some code to read a small sample dataset (obviously if this is possible without having to share some proprietary info. You might have to genericize it a bit) with those vars like that and I'll debug it.