&SQLRC
&SQLRC自动宏变量功能与 &SYSERR相似,但不同之处在于,&SQLRC适用于 SAS程序及 DATA步骤,仅表示SQL程序中最近运行的 SQL程序指令的状态。需要注意的是,&SQLRC仅在 SQL程序指令之后重置,而不是SQL程序——每一个程序都包含多个 SQL程序指令。与 &SYSERR不同,&SQLRC是可读写的,若在同一个SQL程序内被多次评估,则需要重置为“0”。然而,由于&SQLRC未超出 SQL程序边界,因此,在 SQL程序内的第一个 SQL程序指令之前,&SQLRC无须重置为“0”。
以下输出信息的目的是确定数据集中观测数据的数量,但由于 Original数据集不存在,因此目的未能达成。需要注意的是,由于&SYSCC是可读写的,也是可累积的,因此,它必须重置为“0”,然而,&SQLRC在单独使用时则无须进行重置。另外还需要注意,&SQLRC值为“8”表示的是SQL执行过程中的一般性错误 :
%letsyscc=0;
procsqlnoprint;selectcount(*)into:obstotfromoriginal;
quit;
ERROR:TableWORK.ORIGINALdoesn’thaveanycolumns.PROCSQLrequireseachofitstablestohaveatleast1column.
%putSYSERR:&syserr;SYSERR:1012
%putSYSCC:&SYSCC;SYSCC:1012
%putSQLRC:&SQLRC;
SQLRC:8
&SQLRC的一个特性是在每个 SQL 程序指令之后都需要对它进行重置,即便是在同一个 SQL程序内,也需要重置。由于一个 SQL程序中包含多个 SQL程序指令, 因此,如果在 SQL程序结束时评估 &SQLRC,它将反映的是最近一个程序指令的成败。在下面代码中,由于 Nodatanodatanodata数据集不存在,因此,第一个 SQL程序指令未能正常运行,&SQLRC暂时设置为“8”:
datatemp;
lengthchar1$10;run;
%letsyscc=0;procsqlnoprint;
selectcount(*)into:obstot1
fromnodatanodatanodata;*datasetdoesnotexist*;
%putSQLRC:&sqlrc;
%putSYSCC:&syscc;selectcount(*)into:obstot2fromtemp;
quit;
%putSQLRC:&sqlrc;
%putSYSCC:&syscc;
由于第二个 SQL程序指令顺利运行,&SQLRC又从 8重置为 0,从而掩盖了上一个错误。此外,&SYSCC 仅在步骤边界之后设置,因此,在故障后面(但在 QUIT程序指令之前)的&SYSCC值会误保留为“0”。直到在QUIT之后,&SYSCC才能正确地反映出前一个 SQL程序指令的错误 :
%letsyscc=0;procsqlnoprint;
selectcount(*)into:obstot1
fromnodatanodatanodata;*datasetdoesnotexist*;
ERROR:FileWORK.NODATANODATANODATA.DATAdoesnotexist.
%putSQLRC:&sqlrc;SQLRC:8
%putSYSCC:&syscc;
SYSCC:0
selectcount(*)into:obstot2fromtemp;
quit;
%putSQLRC:&sqlrc;SQLRC:0
%putSYSCC:&syscc;
SYSCC:1012
如上所述,&SYSCC能检测到包含单个或多个程序指令的SQL程序中的故障,但 &SYSCC无法分离出 SQL 程序中发出警告或“运行时错误”的位置。因此,为了更明确地评估多指令的 SQL程序,可以评估每个 SQL程序指令之后的 &SQLRC。
&SYSFILRC
自动宏变量 &SYSFILRC表示最近 FILENAME程序指令的顺利运行或故障,若FILENAME运行正确,则&SYSFILRC为“0”。在另一本书中讲到的一样, FILENAME 程序指令的一个用途是评估某个数据集是否被其他用户或过程锁定。以下代码能确定 WORK.Temp 数据集中保存的是共享锁定还是排他锁定。如果没有锁 定,那么数据集可以随意获取使用 :
datatemp;
lengthchar$10;run;
%letsyscc=0;
%letlib=work;
%lettab=temp;
%letfil=%sysfunc(pathname(&lib))\&tab..sas7bdat;filenamemyfile"&fil";
data_null_;
excl=fopen('myfile','U');
ifexcl=1thencallsymput('LOCK','None');
elsedo;
shared=open("&lib..&tab");
ifshared=1thencallsymput('LOCK','Shared');elsecallsymput('LOCK','Exclusive');
end;
run;
%putLOCK:&lock;
%putSYSFILRC:&sysfilrc;
%putSYSCC:&syscc;
在运行过程中,FILENAME程序指令顺利进行(&SYSFILRC是“0”),且数据集没有被锁定(&LOCK是None)。如果Temp数据集不存在,也未出现“运行时错误”,但&SYSFILRC会设置为“1”以反映FILENAME程序指令中存在一个异常情况。以下输出信息进一步说明 &SYSCC的值没有受到 &SYSFILRC或其他文件丢失的影响 :
%putLOCK:&lock;LOCK:None
%putSYSFILRC:&sysfilrc;
SYSFILRC:1
%putSYSCC:&syscc;
SYSCC:0
由于未检查 &SYSFILRC,输出信息错误地显示 Temp数据集中没有锁定,其实数据集并不是没有锁定,而是丢失了。当 FILENAME程序指令出现异常情况时,FOPEN函数会创建Temp.sas7bdat文件,该文件反过来也会(通过 OPEN功能)显示为未锁定状态。这显示了 &SYSFILRC在 FILENAME引用一个丢失文件时的一个重要特点,即生成一个返回码“1”,但是,生成的文件参考是有效的,其一旦被 FOPEN 引用,就会创建出已丢失的文件。这种循环逻辑是非常有必要的,由于要从头开始创建丢失的文件,因此,FOPEN需要一个 FILENAME程序指令生成的有效文件参考。“互斥和信号量”部分会进一步解释逻辑错误的必要性。
若在 FILENAME程序指令之后立即评估&SYSFILRC,便会阻止 FOPEN执行并产生这一故障,而且,尽管创建的文件(Temp.sas7bdat)目前确实出现在 WORK文件库中,但它实际并不是一个真实存在的 SAS数据集,因此,它不能在SAS中打开或删除,只能通过 OS删除。
FILENAME程序指令的另一个常见用途是创建连接 OS的管道命令, 但是,&SYSFILRC 宏变量此时是以不同的方式运行的。在下面的代码中,%GET_FILELIST宏接受一个参数化的文件内容目录,并创建一个空格分隔的宏变量(&FILELIST),该变量包含该文件内容目录中的所有文件。这是一个非常有效的工具,然而,如下面 的输出所示,若参数中指定一个不存在的文件内容目录(C:\neverland),那么这一异常情况会出现在SAS日志中,而不会显示在&SYSFILRC中,&SYSFILRC值为“0”则表示“顺利运行”:
%macroget_filelist(dir=);
%letsyscc=0;
%locali;
%localfil;
%globalfilelist;
%letfilelist=GENERALERROR;filenamefpipe"dir/b&dir*.sas";data_null_;
length filelist $10000;infileftruncover;inputfilename$100.;
filelist=catx('',filelist,filename);callsymput('filelist',filelist);retainfilelist;
run;
%mend;
%get_filelist(dir=c:\neverland\);
NOTE:TheinfileFis:
UnnamedPipeAccessDevice,
PROCESS=dir/bc:\neverland\*.sas,RECFM=V,
LRECL=32767
Stderroutput:
The system cannot find the file specified.NOTE:0recordswerereadfromtheinfileF.
NOTE:DATAstatementused(Totalprocesstime):realtime 0.05seconds
cputime 0.01seconds
264 %putSYSCC:&syscc;SYSCC:0
265 %putSYSERR:&syserr;SYSERR:0
266 %putSYSFILRC:&sysfilrc;
SYSFILRC:0
为了克服 &SYSFILRC的这一缺点,首先需要使用一个不包含PIPE命令的FILENAME 程序指令证实文件内容目录的存在,只有确认了该文件目录的存在,才能发布第二个包含 PIPE命令的 FILENAME程序指令。