Stop SAS EG project if error is encountered - error-handling

I tried putting this before all programs (using the option to include code in pre-processing):
OPTIONS ERRORABEND;
It does trigger a warning popup but the next files do get executed. For example:
A -­> B -> C -> D
If A has an error, I will get a popup saying that an error occured and that the server has disconnected, however, B, C, and D will also run. I want the project execution to stop at A.
I have several old projects with tens if not hundreds of programs and therefore I don't consider using a macro (Is there a way to make SAS stop upon the first warning or error?) while also checking for errors as an option.
What can be done?
Thank you!
Edit: This is for SAS Entreprise Guide 7.1

I think the best way is to write in Macro, like:
%Macro error_check;
'your process for A';
%if &syserr > 0 %then %do;
%goto exit;
%end; /* if your systemerror value is greater than zero, then go to exit directly */
'your process for B';
'your process for C';
'your process for D';
%exit: /* be careful, it is not semicolon*/
%Mend;
Just try NOT to put that %if &syserr loop inside a "data-run statement". I tried to do that and things got just complicated.

Related

How to set macro condition for day of today in any of list

I wanna set the code run at days: 5,11,16,22,28 monthly but it doesn't work.
%if %sysfunc(day(%sysfunc(today()))) in (5,11,16,22,28) %then %do
How can I fix it? Thanks much.
The IN operator works in a SAS 9.2 Macro but you need to define system options. The MINOPERATOR (Macro-In-Operator) must be turned on and in your case, the MINDELILITER too must be specified as you are using comma delimited values.
More information can be found in Getting the IN operator to FUNCTION inside a SAS® Macro
options minoperator mindelimiter=',';
%if %sysfunc(day(%sysfunc(today())-1)) in (5,11,16,22,28) %then %do;
%put do smth...;
%end;
do smth...
Notice the -1 in the above example to replicate March 22.

Automate SAS program for different sites using %include

The problem I have is, I use the same program for reports on multiple sites. I have to run the code for each of them individually. I'm looking to automate my process to call the code and run for all sites in a go. This is the code I tried using but it is not working.
data _null_;
array sites {2} _temporary_ (SiteA SiteB);
do k = 1 to dim(sites);
%let site = sites(k);
%include '...path\SitesWait.sas';
end; run;
The code in SiteWait works perfectly on its own. I defined a macro variable called site, which sets the site for the code to run.
The error I keep getting is 'ERROR 117-185: There was 1 unclosed DO block.'
Thanks
You cannot include another multi-step program into the middle of a DATA step. %include just puts lines from the file into the program stream as if the lines had been typed into program. When SAS sees the first DATA or PROC statement in the included file it will stop compiling the DATA step in your main program and run it. That is why your DO loop is not seeing the END statement.
You could just use a data step to generate code that sets the parameter and includes the program.
data _null_;
length site $20 ;
do site='SiteA','SiteB' ;
call execute(cats('%nrstr(%let) site=',site,';'));
call execute("%include '...path\SitesWait.sas';");
end;
run;
What you have created is basically a macro with a single parameter named SITE. In fact you could possible just use %INCLUDE as the body of the macro definition. Then you could just code the calls you want instead of trying to process some list of values.
%macro siteswait(site);
%include '...path\SitesWait.sas';
%mend;
%siteswait(SiteA)
%siteswait(SiteB)
So you wish to include different files based on prior dataset. I suggest you create the file instead of reading it from array, but keeping with the spirit of your initial request:
%let basePath= c:\foo;
data _NULL_;
array sites {2} $5. ('SiteA' , 'SiteB');
do k = 1 to dim(sites);
site = sites(k);
call execute('%nrstr(%put &basePath.\'||site||';)');
end;
run;
This produces
c:\foo\SiteA
c:\foo\SiteB
You can easily replace the %put command with %include to accomplish what you wish.
Edit: What I suggest you try to do instead is something like this:
data includes;
format path $30. file $10.;
input path $ file $;
cards;
c:\foo\ file1.sas
c:\foo\ file2.sas
c:\Bar\ file1.sas
c:\Bar\ file2.sas
; run;
data _null_;
set includes;
call execute('%nrstr(%put '||strip(path)||''||strip(file)||';)');
run;
You should do like this :
main.sas
%macro ProcessList;
%let list_of_site=SiteA|SiteB|SiteC;
%let k=1;
%do %while (%qscan(&list_of_site, &k,|) ne );
%let site = %scan(&list_of_site, &k,|);
%include 'H:\desktop\SAS\test_inc.sas';
/*%put site=&site;*/
%let k = %eval(&k + 1);
%end;
%mend ProcessList;
%processList;
test_inc.sas
%put site=&site;
Result:
site=SiteA
site=SiteB
site=SiteC
With a macro fonction it is simple. You can replace %include with %put.
You can get the list of site from a dataset with this technic :
data test;
infile datalines dsd;
input site : $200. ;
datalines;
SiteA,
SiteB,
SiteC,
SiteD,
SiteE,
SiteF,
;
run;
proc sql noprint;
select quote(trim(site), "'") into : list_of_site separated by "|" from work.test;
quit;
Regards
If the sites don't change often, and if there are not too many of them, this may be the simplest approach:
%let site=siteA;
%include '...path\SitesWait.sas';
%let site=siteB;
%include '...path\SitesWait.sas';
%let site=siteC;
%include '...path\SitesWait.sas';
Be careful though. You may want to include code at the top of SitesWait.sas that 'resets' you SAS session. ie. clears any macro values you plan on using, deletes any datasets in your work folder etc. Otherwise the second call to SitesWait.sas may be affected by the prior run.

Cancel SQL *PLUS Execution without raising an error

i have a tricky SQL Problem:
We have a huge SQL Script which installs our application on the DB server.
We want to skip the database installation if the latest update didn't change anything in the DB.
I have implemented following check, which is executed before the other SQL commands:
check_current_version_delta.sql:
DECLARE
v_deploy_version VARCHAR2(30) := '&db_deploy_version';
v_check BOOLEAN := FALSE;
BEGIN
DBMS_OUTPUT.PUT_LINE( '--------------------------------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE( 'Check if we have a new DB version');
DBMS_OUTPUT.PUT_LINE( '--------------------------------------------------------------------------------');
FOR cu_version IN (SELECT version FROM &DB_CURRENT_USER..db_deployment WHERE version = v_deploy_version AND ROWNUM = 1) LOOP
v_check := TRUE;
END LOOP;
IF v_check THEN
RAISE_APPLICATION_ERROR( -20001, 'DB Version: '||v_deploy_version||' is already installed');
END IF;
END;
/
This is working very well but our installation team complains about the ORA-XXXXX Error in the log, because they have automated error checks this installation is marked as FAIL (though there was no actual error)
So now the actual problem:
I need to cancel the execution of the SQL without any errors in the LOG. Is this even possible?
Alternative would be to make the rest of the installation dependent on the outcome of the script above. But i'm not sure how to accomplish that.
Have you some suggestions on how to handle it the good way?
One possible way is to create three scripts: The first checks for the condition and either calls the second or the third. The second does the real job. The third is an empty dummy, to avoid the error message cause by calling a non-existent script
In code, it looks like this:
col SCRIPT1_COL new_val SCRIPT1
SELECT case
when version = '&db_deploy_version' then 'dummy.sql'
else 'upgrade_db.sql'
end as SCRIPT1_COL
FROM &DB_CURRENT_USER..db_deployment;
#&SCRIPT1
Alternatively, you could use the method shown above to load either a script "dummy.sql" that does nothing or a script "exit.sql" that just contains the exit command, and execute it before doing the real job.
Presumably you're already using whenever sqlerror so make it terminate when you raise that exception, and you're redirecting the output to your log file. If so you can just hide the text of the exception with set termout off:
whenever sqlerror exit success
set termout off
DECLARE
...
BEGIN
...
IF v_check THEN
RAISE_APPLICATION_ERROR( -20001,
'DB Version: '||v_deploy_version||' is already installed');
END IF;
END;
/
set termout on
whenever sqlerror exit failure
... the rest of your script
The script will stop if the exception is raised but produce no output. The success means anything that runs this won't decide it has errored independently of the log; the exit code from sqlplus will be zero.
You may be spooling to output instead; in which case just don't start the spool until after your check. Or if you have things before this that you do have to spool, turn the spool off and then on again afterwards with append.

File Output using Gforth

As a first project I have been writing a short program to render the Mandelbrot fractal. I have got to the point of trying to output my results to a file ( e.g. .bmp or .ppm ) and got stuck.
I have not really found any examples of exactly what I am trying to do, but I have found two examples of code to copy from one file to another.
The examples in the Gforth documentation ( Section 3.27 ) did not work for me ( winXP ) in fact they seemed to open and create files but not write to files properly.
This is the Gforth documentation example that copies the contents of one file to another:
0 Value fd-in
0 Value fd-out
: open-input ( addr u -- ) r/o open-file throw to fd-in ;
: open-output ( addr u -- ) w/o create-file throw to fd-out ;
s" foo.in" open-input
s" foo.out" open-output
: copy-file ( -- )
begin
line-buffer max-line fd-in read-line throw
while
line-buffer swap fd-out write-line throw
repeat ;
I found this example ( http://rosettacode.org/wiki/File_IO#Forth ) which does work. The main problem is that I can't isolate the part that writes to a file and have it still work. The main confusion is that >r doesn't seem to consume TOS as I might expect.
: copy-file2 ( a1 n1 a2 n2 -- )
r/o open-file throw >r
w/o create-file throw r>
begin
pad maxstring 2 pick read-file throw
?dup while
pad swap 3 pick write-file throw
repeat
close-file throw
close-file throw ;
\ Invoke it like this:
s" output.txt" s" input.txt" copy-file
I would be very grateful if someone could explain exactly how the open, create read and write -file words actually work, as my investigation keeps resulting in somewhat bizarre stacks.
Any clues as to why the Gforth examples do not work might help too.
In summary, I want to output from Gforth to a file and so far have been thwarted. Can anyone offer any help?
Thank you Vijay, I think that I understand the example that you gave. However when I try to use something like this ( which I think is similar ):
0 value test-file
: write-test
s" testfile.out" w/o create-file throw to test-file
s" test text" test-file write-line ;
I get ok but nothing is put into the file, have I made a mistake?
It seems that the problem was due to not flushing the relevant buffers or explicitly closing the file. Adding something like
test-file flush-file throw
or
test-file close-file throw
between write-line and ; makes it work. Consequently the Gforth documentation example would have worked had I followed the instructions.
Thanks again Vijay for helping.
I will try to explain how write-line works with this simple example. Here we have a buffer that contains the string " hello", and we want to write that to a file opened with open-output.
buffer 5 fd-out write-line
5 is the length of the buffer. fd-out is the open file handle. A call to write-line will leave an integer result on the stack, the value of which is implementation dependent. More information on the file I/O words could be found in File Access words.
Calling the word throw is optional. It will check the integer value on the top of the stack and based on that value either pops the topmost exception frame from the exception stack or call abort or display an implementation-dependent message giving information about the condition associated with the integer. (Exact details on how throw works could be found in THROW).
You program is incomplete, which makes the question hard to answer.
With prepending
1024 CONSTANT max-line
CREATE line-buffer max-line 3 + ALLOT
and postpending
copy-file
fd-in close-file throw
fd-out close-file throw
and after creating a testfile foo.in gforth the first program works flawlessly on Debian.
Probably your problem is caused by not properly closing the file after writing.

SAS - Reading a File Backwards?

I need SAS to read many large log files, which are set up to have the most recent activities at the bottom. All I need is the most recent time a particular activity occurred, and I was wondering if it's possible for SAS to skip parsing the (long) beginning parts of the file.
I looked online and found how to read a dataset backwards, but that would require SAS to first parse everything in the .log file into the dataset first. Is it possible to directly read the file starting from the very end so that I can stop the data step as soon as I find the most recent activity of a particular type?
I read up on infile as well, and the firstobs option, but I have no idea how long these log files are until they are parsed, right? Sounds like a catch-22 to me. So is what I'm describing doable?
I'd probably set up a filename pipe statement to use an operating system command like tail -r or tac to present the file in reverse order to SAS. That way SAS can read the file normally and you don't have to worry about how long the file is.
If you mean parsing a sas log file, I am not sure if reading the log file backward is worth the trouble in practice. For instance, the following code executes less than a tenth of a second on my PC and it is writing and reading a 10,000 line log file. How big is your log files and how many are there? Also as shown below, you don't have to "parse" everything on every line. You can selectively read some parts of the line and if it is not what you are looking for, then you can just go to the next line.
%let pwd = %sysfunc(pathname(WORK));
%put pwd=&pwd;
x cd &pwd;
/* test file. more than 10,000 line log file */
data _null_;
file "test.log";
do i = 1 to 1e4;
r = ranuni(0);
put r binary64.;
if r < 0.001 then put "NOTE: not me!";
end;
put "NOTE: find me!";
do until (r<0.1);
r = ranuni(0);
put r binary64.;
end;
stop;
run;
/* find the last line that starts with
NOTE: and get the rest of the line. */
data _null_;
length msg $80;
retain msg;
infile "test.log" lrecl=80 eof=eof truncover;
input head $char5. #;
if head = "NOTE:" then input #6 msg $char80.;
else input;
return;
eof:
put "last note was on line: " _n_ ;
put "and msg was: " msg $80.;
run;
/* on log
last note was on line: 10013
and msg was: find me!
*/