This should be an easy one, but I can't figure it out: there are situations where I need to create a macro variable from contents of a table, and they sometimes contain ampersands (&) as part of the text. How do I get SAS to ignore the ampersands when I call the macro variable? For example, this code...
data _null_;
test="Amos&Andy";
call symputx("testvar",test);
run;
%put testvar=&testvar;
...writes this to the log:
28 data _null_;
29 test="Amos&Andy";
WARNING: Apparent symbolic reference ANDY not resolved.
30 call symputx("testvar",test);
31 run;
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
WARNING: Apparent symbolic reference ANDY not resolved.
32 %put testvar=&testvar;
testvar=Amos&Andy
How can I get SAS to ignore the ampersand and not write the WARNING to the log? Thanks very much!
The first warning is easy to avoid. Macro variable references don't resolve inside of single quotes, so you can use:
data _null_;
test='Amos&Andy';
call symputx("testvar",test);
run;
When you want to resolve &testvar, you need a way to tell the macro processor not to resolve any macro triggers that are revealed by resolving &testvar. This is the purpose of macro quoting:
%put testvar=%superq(testvar);
%SUPERQ tells the macro processor to resolve the macro variable Testvar and to quote (i.e. mask) any macro triggers that are revealed by resolving Testvar. This prevents the macro processor from looking for a macro variable named Andy.
Related
I am trying to get insert the timestamp, current date and current time into a table using macro, but values are not getting displayed as expected. Can someone help on this please?
Also i m trying to write the SQL return code and message, but it displayed nothing.
%MACRO INS;
data _NULL_;
call symput('currdatets',datetime());
call symput('currdate',today());
call symput('currtime',timepart(datetime()));
%put currdatets> &currdatets;
%put currdater--2> &currdate;
%put currtime---2> &currtime;
run;
proc sql;
CONNECT TO DB2
insert into table
(entrytime, rundate, runtime)
values
(&currdatets,&currdate,&currtime)
DISCONNECT FROM DB2;
QUIT;
%PUT &SQLXMSG;
%PUT &SQLXRC ;
%MEND;
WARNING: Apparent symbolic reference CURRDATETS not resolved.
currdatets> &currdatets
WARNING: Apparent symbolic reference CURRDATE not resolved.
currdater--2> &currdate
WARNING: Apparent symbolic reference CURRTIME not resolved.
currtime---2> &currtime
WARNING: Apparent symbolic reference SQLXMSG not resolved.
&SQLXMSG
WARNING: Apparent symbolic reference SQLXRC not resolved.
&SQLXRC
Your first three macro-variables are not resolved because you specified the %put statements before the end of the data _null_ step (i.e., before the run;). symput assigns values produced in a DATA step to macro variables during program execution.
Use symputx instead of symput. It does not change the result though, but
symput gives you a message on the log about the conversion, while symputx does not. Moreover, symputx takes the additional step of removing any leading blanks that were caused by the conversion.
As for the two SQL Pass-Through automatic macro-variables you will need to provide us with more information. I don't know if intended or not, but you seem to use an explicit Pass-Through connection. If so, you might be missing information to connect to the server (e.g., connect to db2 (dsn= "xxxx")).
The automatic macro-variables SQLXRC and SQLXMSG are reset after each SQL Procedure Pass-Through Facility statement has been executed. If they are not resolved it means there were not any.
By the way, according to the documentation, you may want to use %SUPERQ() with SQLXMSG
SQLXMSG contains descriptive information and the DBMS-specific return
code for the error that is returned by the pass-through facility.
Note: Because the value of the SQLXMSG macro variable can contain
special characters (such as &, %, /, *, and ;), use the %SUPERQ macro
function when printing the following value: %put %superq(sqlxmsg);
%macro ins();
data _null_;
call symputx('currdatets',datetime());
call symputx('currdate',today());
call symputx('currtime',timepart(datetime()));
run;
%put currdatets> &currdatets. | currdate> &currdate. | currtime> &currtime.;
proc sql;
connect to db2;
insert into table (entrytime, rundate, runtime)
values (&currdatets,&currdate,&currtime);
disconnect from db2
;
quit;
%put %superq(sqlxmsg);
%put &sqlxrc. ;
%mend;
%ins();
currdatets> 1966238593.2 | currdate> 22757 | currtime> 33793.19107
I have a macro that resolves to a string which contains an ampersand and this cause the error WARNING: Apparent symbolic reference A not resolved.
For example
Data _NULL_;
T=%NRSTR("A&A");
call symput("test",T);
run;
%put &=test.;
Is there a way to only resolve only once? The NR function seems to remove the meaning of all & and prevent any resolutions. I only want it to be resolved once.
The following example works but I need it to be part of a data step as there are several other regex functions that are being used to create the A&Astring.
%let Test=%NRSTR(A&A);
%put &test;
Any ideas?
In the DATA step single quote the text value that is being sent to the macro environment.
To prevent the & from being interpreted as a resolution request, %superq the macro symbol when using it.
Data _NULL_;
T = 'A&A';
call symput("test",T);
run;
%put NOTE: test macro symbol value is %superq(test);
---------- LOG ----------
21 %put NOTE: test macro symbol value is %superq(test);
NOTE: test macro symbol value is A&A
To solve the problem with the first usage just use single quotes. This will allow you create the macro variable with the ampersand in it.
data _null_;
call symputx('test','A&A');
run;
Then use some macro code to add macro quoting. I find %SUPERQ() the easiest.
%let test=%superq(test);
If you are making a lot of macro variables in this way in the same data step you might want to use the RESOLVE() or CALL EXECUTE() function to allow you to run the %LET right after you create the macro variable.
data _null_;
set variable_list;
call symputx(name,value);
call execute(catx(' ','%let',name,'=%superq(',name,');'));
run;
To get the file name of the current SAS program we can write
%put %sysget(SAS_EXECFILEPATH);
I am so unfortunate that someone once created a directory that included an ampersand in the path (yikes!). Let us create an example folder C:\temp&other and store our test.sas program here in that folder.
Now, my question is:
Is it possible to mask the output of %sysget(SAS_EXECFILEPATH) to avoid SAS trying to resolve a macro variable &other?
The easy solution would be to change the path name but things are unfortunately not always easy and I am not allowed to change the folder name.
My attempts:
None of the nr functions I have tried help me, since they mask both % and &. I have tried the following (indented line is the SAS log output). Note that %NRQUOTE outputs the same as the "naked" call.
%put %sysget(SAS_EXECFILEPATH);
/* WARNING: Apparent symbolic reference OTHER not resolved. */
/* C:\temp&other\test.sas */
%put %nrstr(%sysget(SAS_EXECFILEPATH));
/* %nrstr(%sysget(SAS_EXECFILEPATH)) */
%put %nrquote(%sysget(SAS_EXECFILEPATH));
/* WARNING: Apparent symbolic reference OTHER not resolved. */
/* C:\temp&other\test.sas */
%put %superq(%sysget(SAS_EXECFILEPATH));
/* WARNING: Apparent symbolic reference OTHER not resolved.*/
/* ERROR: Invalid symbolic variable name C\TEST&OTHER\TEST.SAS. */
Use a data _null_ step with call symputx to create a macro variable that holds the value of sas_execfilepath. You can then resolve it with %superq().
data _null_;
call symputx('sas_execfilepath', sysget('sas_execfilepath'));
run;
%put %superq(sas_execfilepath);
Log output:
C:\temp&other\test.sas
To eliminate the warning about the undefined macro variable (and worse the possibility of it actually finding and using an existing macro variable) you will need to use the SAS function SYSGET() instead of the macro function %SYSGET().
If you cannot run SAS code then you can use the macro function %QSYSFUNC() to run the SAS function and quote the returned value.
%put %qsysfunc(sysget(SAS_EXECFILEPATH));
If might even be useful to define a macro "function" %qsysget() in your personal autocall library. Perhaps something like this:
%macro qsysget(name);
%if -1=%sysfunc(envlen(&name)) %then %put WARNING: Environment variable "&name" not found.;
%else %qsysfunc(sysget(&name));
%mend;
The variable department has a value of M&S in the data step
set ttt;
DepartmentComp=Compress(DepartmentComp);*For use in making directories;
CALL SYMPUT('ggg',trim((division)));
CALL SYMPUT('fff',trim((Department)))
the log displays
SYMBOLGEN: Macro variable FFF resolves to M&S
WARNING: Apparent symbolic reference S not resolved.
How can I get rid of the warning as I suspect that it affects the program?
Use %superq() to mask the '&' and prevent resolution of '&S'. Here's an example for you:
60 data test;
61 comp = "%superq(fff)";
62 putlog "NOTE: comp=%superq(fff)";
63 run;
NOTE: comp=M&S
NOTE: The data set WORK.TEST has 1 observations and 1 variables.
If you are using those variables in a subsequent datastep you could use symget to avoid premature attempts at resolution (as follows):
data _null_;
division='%myDiv';
department='Food&Drink';
call symputx('ggg',division);
call symputx('fff',department);
run;
data someds;
division=symget('ggg');
department=symget('fff');
putlog division= department=;
run;
Points to note:
Other than raising an error condition (syscc=4) it's difficult to say if that warning will affect your program (if you create a macro variable of &s it would). In any case, it's always best to avoid warnings if at all possible.
You can use symputx instead of symput, which will automatically strip leading / trailing blanks
The %superq approach proposed by floydn is a good one for direct use in macro logic.
I posted a question a while back about trimming a macro variable down that I am using to download a CSV from Yahoo Finance that contains variable information on each pass to the site. The code that was suggested to me to achieve this was as follows:
data _null_;
a = "&testvar.";
call symputx('svar',trim(input(a,$8.)));
run;
That worked great, however I have since needed to redesign the code so that I am declaring multiple macro variables and submitting multiple ones at the same time.
To declare multiple macros at the same time I have used the following lines of code:
%let svar&e. = &svar.;
%put stock_ticker = &&svar&e.;
The varible &e. is an iterative variable that goes up by one everytime. This declares what looks to be an identical macro to the one called &svar. everytime they are put into the log, however the new dynamic macro is now throwing up the original warning message of:
WARNING: The quoted string currently being processed has become more than 262 characters long. You
may have unbalanced quotation marks.
That i was getting before i started using the symputx option suggested in my original problem.
The full code for this particular nested macro is listed below:
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
%do e = &beg_point. %to &end_point. %by 1;
%put stock row in dataset nasdaq ticker = &e.;
%global svar&e;
proc sql noprint;
select symbol
into :testvar
from nasdaq_ticker
where monotonic() = &e.;
quit;
/*convert value to string here*/
data _null_;
a = "&testvar.";
call symputx('svar',trim(input(a,$8.)));
run;
%let svar&e. = &svar.;
%put stock_ticker = &&svar&e.;
%end;
%mend;
%symbol_var;
Anyone have any suggestions how I could declare the macro &&svar&e. directly into the call synputx step? It currently throws up an error saying that the macro variable being created cannot contain any special characters. Ive tried using "E, %NRQUOTE and %NRBQUOTE but either I have used the function in an invalid context or I haven't got the syntax exactly right.
Thanks
Isn't this as simple as the following two line data step?
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
data _null_;
set nasdaq_ticker(firstobs=&beg_point. obs=&end_point.);
call symputx('svar' || strip(_n_), symbol);
run;
%mend;
%symbol_var;
Or the following (which includes debugging output)
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
data _null_;
set nasdaq_ticker(firstobs=&beg_point. obs=&end_point.);
length varname $ 32;
varname = 'svar' || strip(_n_);
call symputx(varname, symbol);
put varname '= ' symbol;
run;
%mend;
%symbol_var;
When manipulating macro variables and desiring bullet-proof code I often find myself reverting to using a data null step. The original post included the problem about a quoted string warning. This happens because the SAS macro parser does not hide the value of your macro variables from the syntax scanner. This means that your data (stored in macro vars) can create syntax errors in your program because SAS attempts to interpret it as code (shudder!). It really makes the hair on the back of my neck stand up to risk my program at the hands of what might be in the data. Using the data step and functions protects you from this completely. You will note that my code never uses an ampersand character other than the observation window points. This makes my code bullet proof regarding what dirty data there may be in the nasdaq_ticker data set.
Also, it is important to point out that both Dom and I wrote code that makes one pass over the nasdaq_ticker data set. Not to bash the original posted code, but looping in that way causes a proc sql invocation for every observation in the result set. This will create very poor performance for large result sets. I recommend developing an awareness of how many times a macro loop is going to cause you to read a data set. I have been bitten by this many times in my own code.
Try
call symputx("svar&e",trim(input(a,$8.)));
You need double quotes ("") to resolve the e macro.
As an aside, I am not sure you need the input statement if $testvar is a string and not a number.
I would have written this as
%macro whatever();
proc sql noprint;
select count(*)
into :n
from nasdaq_ticker;
select strip(symbol)
into :svar1 - :svar%left(&n)
from nasdaq_ticker;
quit;
%do i=1 %to &n;
%put stock_ticker = &&svar&i;
%end;
%mend;