笔者使用的SAS数据库查询平台,对接了一个以JSON格式的分级授权引擎,需将用户提交给数据库查询的SQL语句拼接在JSON入参里,但大量用户使用的数据库查询语句SQL超长(超过32767字节),这里,需把超长的SQL语句保存为程序文件。一般情况下,数据库SQL查询语句使用了大量的参数变量,但分级授权引擎不接受传入变量的语法。
如何在SASEG提交给分级授权引擎前将程序文件里的变量解析成变量值呢?笔者尝试了多种方法,终于找到了一种可行有效的方法。
尝试一、直接变量替换法
思路很简单,直接将变量替换为变量值。举例如下:
简单的SQL查询语句(带变量name1)
select name,sex,age,height,weight
from sashelp.class
where name = &name1.
SAS EG里处理程序如下:
filename file1 "C:\saswork\sasdev\EGProject\resolve_var\prog1.sas";
filename file2 "C:\saswork\sasdev\EGProject\resolve_var\prog2.sas";
%let name1='简';
data _null_;
infile file1;
file file2;
input;
length str1 $32767;
str1=tranwrd(_infile,'&name1.',"&name1.");
put str1;
run;
这里,直接将程序文件中的变量&name1.,用替换函数tranwrd,替换为变量的值。但这种方法有个致命的缺陷,无法知道SQL查询语句中具体的变量名和个数。此路不通。
尝试二、使用宏变量自动解析变量的机制
正好使用SAS宏变量会自动解析变量的原理,将程序文件读入到SAS数据集中,然后赋值给宏变量,这样可自动解析成变量值,巧妙地规避了要知晓具体变量名的问题。
简单测试代码如下:
options symbolgen mprint mlogic;
%let name1='简';
%let str1=%str(select name,age from sashelp.class where name = &name1.);
%put &str1;
返回结果如下:
25 options symbolgen mprint mlogic;
26 %let name1='简';
27 %let str1=%str(select name,age from sashelp.class where name = &name1.);
SYMBOLGEN: 宏变量 NAME1 解析为 '简'
28 %put &str1;
SYMBOLGEN: 宏变量 STR1 解析为 select name,age from sashelp.class where name = '简'
SYMBOLGEN: 上一值中宏应引用的某些字符在显示时未加引用。
select name,age from sashelp.class where name = '简'
一种有效可行的程序文件变量解析方法,呼之欲出了,经过笔者几天的奋力coding、debugging、testing。。。终于能端出来啦。
直接上代码吧。
%macro pgm_resolve_vars;
options noquotelenmax;
filename file1 "C:\saswork\sasdev\EGProject\resolve_var\prog1.sas";
filename file2 "C:\saswork\sasdev\EGProject\resolve_var\prog2.sas";
%let name1='简';
/*读取程序文件到数据集temp1,并设置变量解析标识var_flag*/
data temp1;
infile file1 truncover lrecl=32767;
input;
length str1 $32767;
str1=_infile_;
length var_flag $1;
if index(str1,'&')>0 then var_flag='1';
else var_flag='0';
run;
/*过滤空行,判断有变量解析行就设置pgm_flag为1*/
data temp2;
set temp1(where=(lengthn(str1)>0)) end=eof;
retain pgm_flag;
if var_flag='1' then pgm_flag='1';
index=_N_;
if eof then call symputx('pgm_flag',pgm_flag);
run;
/*生成只包含需变量解析的行*/
data temp3(keep=text1 index var_idx);
set temp2(where=(var_flag='1')) end=eof;
rename str1=text1;
var_idx=_N_;
if eof then call symputx('totnum',_N_);
run;
/*循环解析变量,更新到数据集temp2中*/
%do i=1 %to &totnum;
data _null_;
set temp3(where=(var_idx=&i));
call symputx("col&i",text1);
call symputx("num&i",index);
call symputx('dq_flag',find(text1,'"',1);/*判断是否包含双引号*/
run;
%if &dq_flag>0 %then %do;
%let text2=%bquote(&&col&i.);/*使用宏函数避免读取包含双引号字符串的问题*/
data temp2;
set temp2;
if index=&&num&i then str1="&text2";
run;
%end;
%else %do;
%let text2="&&col&i.";
data temp2;
set temp2;
if index=&&num&i then str1=&text2;
run;
%end;
%end;
/*将临时表temp2中所有内容输出到程序文件file2*/
data _null_;
file file2;
set temp2;
put str1;
run;
/*查看变量解析后程序文件*/
data _null_;
infile file2;
input;
put _infile_;
run;
/*清理临时数据集*/
proc datasets lib=work memtype=data kill nolist;quit;
%mend;
options symbolgen mprint mlogic;
%pgm_resolve_vars
希望对你有帮助,也非常欢迎进一步讨论学习!