SAS %errmail macro - sas

Although I have the error handling:
filename myfile email
to=&e_mail.
subject= "Error: &number."
type="text/plain";
%macro errmail;
%if &syserr ne 0 %then %do;
options obs= max replace nosyntaxcheck;
data _null_;
file myfile;
put;
put 'ERROR';
put "&syserrortext";
put 'check a log'
run;
%abort cancel;
%end;
%mend errmail;
when I have the error in the proc export:
(&number. is the table in the work)
proc export data=&number.
outfile="/usr/local/backup/&number./&number._%SYSFUNC(TODAY(),DATE9.).txt"
replace
dbms=dlm;
delimiter=';';
run;
%errmail;
ERROR: Physical file does not exist, /usr/local/backup/2116/2116_13MAY2016.txt.
NOTE: The SAS System stopped processing this step because of errors.
NOTE: There were 1 observations read from the data set WORK.2116.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds
SAS dosen't check a macro and doesn't send an e-mail. I have an error in the path, so I understand the error, but I need an e-mail about that and I want that SAS stops processing because of macro.
Adding some options to the macro can fix it?

this is how I send out my email from SAS
not required to create a file on the system
filename outbox email "email#address.com";
data _null_;
file outbox
to=("email#address.com")
cc=("email#address.com")
subject="SUBJECT OF EMAIL "
/* attach=("C:\sas\results.out" "C:\sas\code.sas") */ ; /* incase you need to attach a file */
put 'Hello! ';
put ' ';
put 'Thank you & let me know if there is anything,';
put "&macrovar."; /*in case you need to put some detail in the email from macro variables make sure to use double quote for that */
put 'SIGNATURE ME';
run;

Related

How to create a MACRO to create a JSON file from dataset

I have some problems when I'm trying to generate a JSON file from dataset on SAS GUIDE. I generated a TEST.JSON:
{"TP_SMS":"1" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"2" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"3" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"4" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"5" "NM_REMETENTESMS":"00000159"},
.
.
.
{"TP_SMS":"9" "NM_REMETENTESMS":"00000159"},
The field TP_SMS is filled correct, but the second field is wrong - they are considering just the last position from my table.
Below there is my code white macro:
data teste30;
set MATABLES.EXIT_DATA;
RESP=cat(CD_CLIENTE,"|",ANWER_DATA);
ID=_N_;
call symputx('ID',ID);
call symputx('CD_CLIENTE',CD_CLIENTE);
call symputx('NM_PRIMNOMECLIENTE',NM_PRIMNOMECLIENTE);
call symputx('RESP',RESP);
call symputx('msgtext',msgtext);
run;
%macro MontaJSON(ID);
WRITE OPEN OBJECT;
WRITE VALUES "TP_SMS" "&ID";
WRITE VALUES "NM_REMETENTESMS" "&CD_CLIENTE";
WRITE CLOSE;
%mend MontaJSON(ID);
%macro SMSRecords;
%do i = 1 %to &dim_IDs;
%MontaJSON(&&&ID_&i);
%end;
%mend SMSRecords;
proc sql;
select id, CD_CLIENTE into :ID_1 - :ID_&SysMaxLong from work.teste30;
%let dim_IDs = &sqlObs;
quit;
proc json out="C:\TEMP\TEST.json" pretty nokeys nosastags;
write open array; /* container for all the data */
%SMSRecords;
write close; /* container for all the data */
run;
I expect this macro get all datas on sequence, as TP_SMS code:
{"TP_SMS":"1" "NM_REMETENTESMS":"00014578"},
{"TP_SMS":"2" "NM_REMETENTESMS":"21323445"},
{"TP_SMS":"3" "NM_REMETENTESMS":"23456753"},
{"TP_SMS":"4" "NM_REMETENTESMS":"00457663"},
{"TP_SMS":"5" "NM_REMETENTESMS":"00014795"},
{"TP_SMS":"6" "NM_REMETENTESMS":"00014566"},
{"TP_SMS":"7" "NM_REMETENTESMS":"00014578"},
{"TP_SMS":"8" "NM_REMETENTESMS":"00000122"},
{"TP_SMS":"9" "NM_REMETENTESMS":"00000159"}
Does anyone has some idea to solve it?
Tks
I would avoid generating all of those macro variables that are confusing your code.
Instead for that simple format you can just write the file directly instead of using PROC JSON.
data _null_;
set MATABLES.EXIT_DATA end=eof;
file "C:\TEMP\TEST.json" ;
if _n_=1 then put '[';
if not eof then delim=',';
put '{"TP_SMS":' id :$quote. ' "NM_REMETENTESMS":' CD_CLIENTE :$quote. '}' delim ;
if eof then put ']';
run;
Or if you really find that PROC JSON helps then use a similar data step to write the lines of code and use %INCLUDE to run the generated code.
filename code temp;
data _null_;
set MATABLES.EXIT_DATA ;
file code ;
put 'WRITE OPEN OBJECT;'
/ 'WRITE VALUES "TP_SMS" ' ID :$quote. ';'
/ 'WRITE VALUES "NM_REMETENTESMS" ' CD_CLIENTE :$quote. ';'
/ 'WRITE CLOSE;'
;
run;
proc json out="C:\TEMP\TEST.json" pretty nokeys nosastags;
write open array; /* container for all the data */
%include code;
write close; /* container for all the data */
run;
This line is your problem, it will only hold the last data point.
call symputx('CD_CLIENTE',CD_CLIENTE);
Instead, create a value for each ID, similar to how you created the IDs.
call symputx(catx('_', 'CD_CLIENTE', put(i, 8.-l)), CD_CLIENTE);
Then use it later on as &&&CD_CLIENTE&i

ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required

I am having errors as below.
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: &N_GROUP
ERROR: The %TO value of the %DO I loop is invalid.
ERROR: The macro ORDERFLOW will stop executing.
When I tested this on local without remote submitting, it worked. But it doesn't work on WRDS server. I have tried many things to fix this but cannot find out what's wrong.
Here is my code. FYI, this is a pilot code for something bigger.
%macro orderflow(YYYYMMDD=, CUTOFF=) /des = "Create OrderFlow";
%syslput YYYYMMDD = &YYYYMMDD;
%syslput CUTOFF = &CUTOFF;
%let wrds = wrds.wharton.upenn.edu 4016;
options comamid=TCP;
signon wrds username=_prompt_;
rsubmit;
/* Enter your WRDS institution name and your WRDS username */
options errors=2;
/* STEP 1: RETRIEVE DAILY TRADE AND QUOTE (DTAQ) FILES */
libname nbbo '/wrds/nyse/sasdata/taqms/nbbo';
libname cq '/wrds/nyse/sasdata/taqms/cq';
libname ct '/wrds/nyse/sasdata/taqms/ct';
libname mast '/wrds/nyse/sasdata/taqms/mast';
/* Create StockList each having 100 stocks */
proc sql noprint;
select ceil(count(SYMBOL_ROOT)/&CUTOFF) into :N_GROUP
from mast.mastm_&YYYYMMDD
where LISTED_MARKET in ('A' 'N' 'T' 'Q') /* AMEX, NYSE, NASDAQ */
and TAPE = 'A' /* Common stock */
; quit;
%do i=1 %to &N_GROUP;
%global STOCKLIST&i;
proc sql noprint;
select SYMBOL_ROOT into :STOCKLIST&i separated by '" "'
from mast.mastm_&YYYYMMDD
where LISTED_MARKET in ('A' 'N' 'T' 'Q') /* AMEX, NYSE, NASDAQ */
and TAPE = 'A' /* Common stock */
and monotonic() between &cutoff*(&i-1)+1 and &cutoff*&i
; quit;
/* Retrieve NBBO data */
data DailyNBBO;
/* Enter NBBO file names in YYYYMMDD format for the dates you want */
set nbbo.nbbom_&YYYYMMDD;
/* Enter company tickers you want */
where sym_root in ("&&STOCKLIST&i") and
/* Quotes are retrieved prior to market open time to ensure NBBO
Quotes are available for beginning of the day trades */
(("9:00:00.000000000"t) <= time_m <= ("9:30:00.000000000"t));
format date date9.;
format time_m part_time trf_time TIME20.9;
run;
/* Retrieve Quote data */
data DailyQuote;
/* Enter Quote file names in YYYYMMDD format for the same dates */
set cq.cqm_&YYYYMMDD;
/* Enter the same company tickers as above */
where sym_root in ("&&STOCKLIST&i") and
/* Quotes are retrieved prior to market open time to ensure NBBO
Quotes are available for beginning of the day trades*/
(("9:00:00.000000000"t) <= time_m <= ("9:30:00.000000000"t));
format date date9.;
format time_m part_time trf_time TIME20.9;
run;
/* Retrieve Trade data */
data DailyTrade;
/* Enter Trade file names in YYYYMMDD format for the same dates */
set ct.ctm_&YYYYMMDD;
/* Enter the same company tickers as above */
where sym_root in ("&&STOCKLIST&i") and
/* Retrieve trades during normal market hours */
(("9:30:00.000000000"t) <= time_m <= ("9:30:00.000000000"t));
type='T';
format date date9.;
format time_m part_time trf_time TIME20.9;
run;
/* Download to PC */
proc download data=DailyNBBO out=taq.DailyNBBO_&&YYYYMMDD&i; run;
proc download data=DailyQuote out=taq.DailyQuote_&&YYYYMMDD&i; run;
proc download data=DailyTrade out=taq.DailyTrade_&&YYYYMMDD&i; run;
%end;
%mend orderflow;
%orderflow(YYYYMMDD=20141224,CUTOFF=100);
Any comments appreciated. Thanks.
This macro is constructed to run on the LOCAL machine since it includes both the SIGNON and RSUBMIT/ENDRSUBMIT blocks.
But you are referencing a macro variable N_GROUP that was created on the remote machine via an SQL INTO clause. I assume that is the reason for the error messages.
Either define the macro to run on the remote machine or use %SYSRPUT to move the macro variable value back to the local machine so you can use it to control the %DO loop.

SAS Data Step if statement not working

With the below code the email always gets sent. 1 obviously does not equal 0, but yet it still runs. I have tried removing the do part but still get the same issue.
data _null_;
set TestTable;
if 1 = 0 then do;
file sendit email
to=("email#gmail.com")
subject="Some Subject Line";
end;
run;
While the file statement is considered an executable statement (and thus should not be executed when behind a false if statement), that is not really entirely true. SAS sees the file statement during compilation, and knows that it needs to create a file to write to - and thus, it's somewhat compile-time. That's what is happening here - SAS creates the file (in this case, the email) as a result of the compiler's activity, then doesn't actually populate it with anything, but still has an email at the end of the day.
The same thing happens with any other file - like so:
data _null_;
set sashelp.class;
if 0 then do;
file "c:\temp\test_non_zero.txt";
put name $;
end;
run;
A blank file is created by that code.
If you need to conditionally send emails, I would recommend wrapping your email code in a macro, and then calling that macro using call execute or similar from the dataset. Like so:
%macro write_email(parameters);
data _null_;
file sendit email
to=("email#gmail.com")
subject="Some Subject Line";
run;
%mend write_email;
data _null_;
set TestTable;
if 0 then do;
call execute('%write_email(parameters)');
end;
run;
Use email directives to abort the message.
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a002058232.htm
data _null_;
file sendit email to=("email#gmail.com") subject="Some Subject Line";
if nobs=0 then put '!EM_ABORT!';
set TestTable nobs=nobs;
....
run;

A macro to send a email

my code to send mail using sas data step. here trying to create a code to send mail.
FILENAME outbox EMAIL ("***********");
DATA _NULL_;
FILE outbox
TO=("************")
FROM=("***********")
SUBJECT=("Example of a SAS E-mail" );
/* ATTACH=(""); */
PUT " ";
PUT "Hello Boss,";
PUT " ";
PUT "Attached are the Daily Operational Reports.";
PUT " ";
PUT "rrt";
RUN;
I don't tend to include email parameters in the datastep itself, but rather in fileref. I have tested the code below with my email address and it worked.
As I said in the comments, you simply need to wrap your datastep in a macro if you want it to run within macro.
You can have positional or keyword parameters. See this source for detailed info. I have used keyword parameters in this example. you call the macro by specifying the keyword and then the value like I showed the last line. If you don't put anything, then the keyword gets ignored.
By the way, the empty keywords result in initialised local macro variables. Let statements like ( %LET FROM=FROM="&FROM") are there to add a string "FROM=" to the beginning of "FROM" macro variable so the filename syntax would be complete. If macro variable such as "FROM" would be passed as is, it would resolve to email address that filename woudn't know what to do with.
%MACRO send_email (TO=,FROM=,SUBJECT=,ATTACHMENT=,BODY=);
%IF &TO ne %THEN
%LET TO="&TO";
%IF &FROM ne %THEN
%LET FROM=FROM="&FROM";
%IF &SUBJECT ne %THEN
%LET SUBJECT=SUBJECT="&SUBJECT";
%IF &ATTACHMENT ne %THEN
%LET ATTACHMENT=ATTACHMENT="&ATTACHMENT";
%IF &BODY ne %THEN
%LET BODY="&BODY";
FILENAME outbox EMAIL &TO &FROM &SUBJECT &ATTACHMENT;
DATA _NULL_;
FILE outbox;
PUT &BODY;
RUN;
%MEND;
%send_email(email=example#example.com);
Peeyush,
Try the code sample below. Make sure to enter your SMTP email host details and the email addresses to and from. You can make the email addresses macro variables if you want additional automation:
%macro emailM;
OPTIONS emailsys = SMTP emailhost = Youremail.Host.net emailport= 25;
FILENAME Mailbox EMAIL 'You#something.com';
attach=("\\somewhere\something\File.xls");
DATA _NULL_;
FILE MailBox TO=("somebody#something.com" "somebody2#something.com")
Subject="Example of a SAS E-mail";
PUT "Hello Boss,";
PUT " ";
PUT "Attached are the Daily Operational Reports.";
PUT " ";
PUT "Thank you";
RUN;
%mend emailM;
%emailM;

Repeatedly running a data step if URL fails to load

I am creating a dataset using filename URL web submissions. However, in some instaces I keep getting '502' responses from the server. To get around this I would like to use some conditional logic inside a macro. I'm most of the way there, but I cant quite get the end bit to work. The idea is that the macro, which is nested within other nested macros will keep trying this one submission until it gets a dataset that doesn't have 0 observations then move on:
%macro test_exst;
filename loader url "http://finance.yahoo.com/d/quotes.csv?s=&svar1.+&svar2.+&svar3.+&svar4.+&svar5.+&svar6.+&svar7.+&svar8.+&svar9.+&svar10.+
&svar11.+&svar12.+&svar13.+&svar14.+&svar15.+&svar16.+&svar17.+&svar18.+&svar19.+&svar20.+
&svar21.+&svar22.+&svar23.+&svar24.+&svar25.+&svar26.+&svar27.+&svar28.+&svar29.+&svar30.+
&svar31.+&svar32.+&svar33.+&svar34.+&svar35.+&svar36.+&svar37.+&svar38.+&svar39.+&svar40.+
&svar41.+&svar42.+&svar43.+&svar44.+&svar45.+&svar46.+&svar47.+&svar48.+&svar49.+&svar50.+
&svar51.+&svar52.+&svar53.+&svar54.+&svar55.+&svar56.+&svar57.+&svar58.+&svar59.+&svar60.+
&svar61.+&svar62.+&svar63.+&svar64.+&svar65.+&svar66.+&svar67.+&svar68.+&svar69.+&svar70.+
&svar71.+&svar72.+&svar73.+&svar74.+&svar75.+&svar76.+&svar77.+&svar78.+&svar79.+&svar80.+
&svar81.+&svar82.+&svar83.+&svar84.+&svar85.+&svar86.+&svar87.+&svar88.+&svar89.+&svar90.+
&svar91.+&svar92.+&svar93.+&svar94.+&svar95.+&svar96.+&svar97.+&svar98.+&svar99.+&svar100.+
&svar101.+&svar102.+&svar103.+&svar104.+&svar105.+&svar106.+&svar107.+&svar108.+&svar109.+&svar110.+
&svar111.+&svar112.+&svar113.+&svar114.+&svar115.+&svar116.+&svar117.+&svar118.+&svar119.+&svar120.+
&svar121.+&svar122.+&svar123.+&svar124.+&svar125.+&svar126.+&svar127.+&svar128.+&svar129.+&svar130.+
&svar131.+&svar132.+&svar133.+&svar134.+&svar135.+&svar136.+&svar137.+&svar138.+&svar139.+&svar140.+
&svar141.+&svar142.+&svar143.+&svar144.+&svar145.+&svar146.+&svar147.+&svar148.+&svar149.+&svar150.+
&svar151.+&svar152.+&svar153.+&svar154.+&svar155.+&svar156.+&svar157.+&svar158.+&svar159.+&svar160.+
&svar161.+&svar162.+&svar163.+&svar164.+&svar165.+&svar166.+&svar167.+&svar168.+&svar169.+&svar170.+
&svar171.+&svar172.+&svar173.+&svar174.+&svar175.+&svar176.+&svar177.+&svar178.+&svar179.+&svar180.+
&svar181.+&svar182.+&svar183.+&svar184.+&svar185.+&svar186.+&svar187.+&svar188.+&svar189.+&svar190.+
&svar191.+&svar192.+&svar193.+&svar194.+&svar195.+&svar196.+&svar197.+&svar198.+&svar199.+&svar200.
&f=&&fvar&a." DEBUG ;
/* data step based on filename url above goes here, each pass will give 500 metrics x 1 symbol dataset*/
%put create dataset from csv submission;
data temp_&I._&&fvar&a.;
infile loader length=len MISSOVER /*delimiter = ','*/;
/* input record $varying8192. len; */
input record $varying30. len;
format record $30.;
informat record $30.;
run;
data _null_;
dsid=open("temp_&I._&&fvar&a.");
obs=attrn(dsid,"nobs");
put "number of observations = " obs;
if obs = 0 then stop;
else;
filename loader url "http://finance.yahoo.com/d/quotes.csv?s=&svar1.+&svar2.+&svar3.+&svar4.+&svar5.+&svar6.+&svar7.+&svar8.+&svar9.+&svar10.+
&svar11.+&svar12.+&svar13.+&svar14.+&svar15.+&svar16.+&svar17.+&svar18.+&svar19.+&svar20.+
&svar21.+&svar22.+&svar23.+&svar24.+&svar25.+&svar26.+&svar27.+&svar28.+&svar29.+&svar30.+
&svar31.+&svar32.+&svar33.+&svar34.+&svar35.+&svar36.+&svar37.+&svar38.+&svar39.+&svar40.+
&svar41.+&svar42.+&svar43.+&svar44.+&svar45.+&svar46.+&svar47.+&svar48.+&svar49.+&svar50.+
&svar51.+&svar52.+&svar53.+&svar54.+&svar55.+&svar56.+&svar57.+&svar58.+&svar59.+&svar60.+
&svar61.+&svar62.+&svar63.+&svar64.+&svar65.+&svar66.+&svar67.+&svar68.+&svar69.+&svar70.+
&svar71.+&svar72.+&svar73.+&svar74.+&svar75.+&svar76.+&svar77.+&svar78.+&svar79.+&svar80.+
&svar81.+&svar82.+&svar83.+&svar84.+&svar85.+&svar86.+&svar87.+&svar88.+&svar89.+&svar90.+
&svar91.+&svar92.+&svar93.+&svar94.+&svar95.+&svar96.+&svar97.+&svar98.+&svar99.+&svar100.+
&svar101.+&svar102.+&svar103.+&svar104.+&svar105.+&svar106.+&svar107.+&svar108.+&svar109.+&svar110.+
&svar111.+&svar112.+&svar113.+&svar114.+&svar115.+&svar116.+&svar117.+&svar118.+&svar119.+&svar120.+
&svar121.+&svar122.+&svar123.+&svar124.+&svar125.+&svar126.+&svar127.+&svar128.+&svar129.+&svar130.+
&svar131.+&svar132.+&svar133.+&svar134.+&svar135.+&svar136.+&svar137.+&svar138.+&svar139.+&svar140.+
&svar141.+&svar142.+&svar143.+&svar144.+&svar145.+&svar146.+&svar147.+&svar148.+&svar149.+&svar150.+
&svar151.+&svar152.+&svar153.+&svar154.+&svar155.+&svar156.+&svar157.+&svar158.+&svar159.+&svar160.+
&svar161.+&svar162.+&svar163.+&svar164.+&svar165.+&svar166.+&svar167.+&svar168.+&svar169.+&svar170.+
&svar171.+&svar172.+&svar173.+&svar174.+&svar175.+&svar176.+&svar177.+&svar178.+&svar179.+&svar180.+
&svar181.+&svar182.+&svar183.+&svar184.+&svar185.+&svar186.+&svar187.+&svar188.+&svar189.+&svar190.+
&svar191.+&svar192.+&svar193.+&svar194.+&svar195.+&svar196.+&svar197.+&svar198.+&svar199.+&svar200.
&f=&&fvar&a." DEBUG ;
data temp_&I._&&fvar&a.;
infile loader length=len MISSOVER /*delimiter = ','*/;
/* input record $varying8192. len; */
input record $varying30. len;
format record $30.;
informat record $30.;
run;
run;
%mend;
%test_exst;
The idea here is try URL submission, create dataset from it, check number of obs is not zero. If its not end the macro. If it is resubmit the same filename URL then create the dataset from it again. Keep doing this until the server respond, the exit the macro and most on to the rest of the code.
I haven't got as far as running this code in anger yet. I'm guessing the filename URL will work fine, but it is the fact that the code is attempting to create a dataset within a data null step right at the end that is making it fall over. Any ideas?
Thanks
Without getting into the specifics of your project, a good way to approach this generally is with recursion.
%macro test_me(iter);
%let iter=%eval(&iter.+1);
data my_data;
infile myfilename;
input stuff;
call symputx("obscount",_n_);
run;
%if &obscount=1 and &iter. < 10 %then %do;
%put Iteration &iter. failed, trying again;
%test_me(&iter.);
%end;
%mend test_me;
%test_me(0);
It checks to see if it worked, and if it did not work, it calls itself again, with a maximum iteration count to make sure you don't end up in infinite loop land if the server is down or somesuch. You also might put a delay in there if the server has a maximum frequency of calls or any other rules the API requires you to follow.