This is driving me mad now. I am trying to mask all the special characters in a particular line of a SAS plan file being used during a grid installation that I have pulled into a dataset:
if test_item = %str(<Machine Id=%'$machine:)&mach_num.%str(' Name=')&mach_name.%str(%'>) then end2 = end1;
The log is printed below. I see to be getting spool errors (possibly because something has not been masked correctly), however the MPRINT to the log suggests that the macro variable compilation has executed correctly.
SYMBOLGEN: Macro variable FUNC_VAR resolves to test
MPRINT(PLAN_FINDER): data test_plan4;
SYMBOLGEN: Macro variable FUNC_VAR resolves to test
MPRINT(PLAN_FINDER): set test_plan3;
MPRINT(PLAN_FINDER): by ret_sort;
MPRINT(PLAN_FINDER): retain end2;
388: LINE and COLUMN cannot be determined.
NOTE: NOSPOOL is on. Rerunning with OPTION SPOOL might allow recovery of the LINE and COLUMN where the error has occurred.
ERROR 388-185: Expecting an arithmetic operator.
SYMBOLGEN: Macro variable MACH_NUM resolves to 2
SYMBOLGEN: Macro variable MACH_NAME resolves to Metadata Server
76: LINE and COLUMN cannot be determined.
NOTE: NOSPOOL is on. Rerunning with OPTION SPOOL might allow recovery of the LINE and COLUMN where the error has occurred.
ERROR 76-322: Syntax error, statement will be ignored.
________
49
MPRINT(PLAN_FINDER): if test_item = <Machine Id='$machine:2' Name='Metadata Server'> then end2 = end1;
MPRINT(PLAN_FINDER): else if end1 = 1 then end2 = end1 + end2;
MPRINT(PLAN_FINDER): run;
Can anyone advise what I have done incorrectly?
The code:
%macro test(func_var, mach_num, mach_name);
data &func_var._plan4;
set &func_var._plan3;
by ret_sort;
retain end2;
if test_item = %str(<Machine Id=%'$machine:)&mach_num.%str(' Name=')&mach_name.%str(%'>) then end2 = end1;
else if end1 = 1 then end2 = end1 + end2;
run;
%mend;
%test(test, 2, Metadata Server);
Expected resolution of string in test_item:
<Machine Id='$machine:2' Name='Metadata Server'>
Thanks
I don't know why you think you need the masking. It's possible it's more complex than I think but it also seems like a lot of unnecessary work to me.
This works as expected for me, let me know how if it works for what you're trying to do.
test_item = "<Machine Id='$machine:&mach_num.' Name='&mach_name'>"
Test in a data step:
%let mach_name=Metadata Server;
%let mach_num=2;
data test;
test_item = "<Machine Id='$machine:&mach_num.' Name='&mach_name'>" ;
run;
proc print data=test;
run;
Results:
test_item = <Machine Id='$machine:2' Name='Metadata Server'>
Related
I need to store a value from a table into a variable. I've tried let, symputx, and select into. In the current version I try to use symputx, but the variable is not being updated. The products table contains type, price_floor, price_tier1, price_tier2.
%global price1;
%global price2;
%macro container();
DATA _null_;
SET products;
IF type = "Single" THEN DO;
CALL SYMPUTX('price1', price_floor,'g');
END;
IF type = "Multi" THEN DO;
CALL SYMPUTX('price1', price_tier1,'g');
CALL SYMPUTX('price2', price_tier2,'g');
END;
%PUT &=price1;
%PUT &=price2;
%mend;
Both price1 and price2 are null.
SYMBOLGEN: MACRO variable PRICE1 resolves to PRICE1=
SYMBOLGEN: MACRO variable PRICE2 resolves to PRICE2=
You don't have a run statement on your datastep, so your %put statement is being written to the log before the data step executes - so, the variables don't exist yet. It won't be run until you do provide a run statement or step boundary, or SAS might do that politely for you when the program is finished, but either way it's not being run before the %put.
Usually, this kind of program is an anti-pattern in SAS; you don't provide sufficient context, so maybe it's okay, but this will only work if you have only one row in the dataset - otherwise it probably won't do anything useful. The key word that's triggering me to write this is that you called this a "variable" - not a "macro variable" - in your question; SAS macro variables are not really "variables" and not meant to be used like a C variable or Python variable.
FYI to anyone interest, I decided to restructure the program as a whole. The original plan was to pass elements of a user defined array into %container, then use the assigned macro variables price1 and price2 as parameter in another macro call.
Instead of imbedding this data step in a macro, I created a table to contain all of the inputs I planned to pass into %container. Then I just used executes with the table variables concatenated within instead of direct macro calls.
data _null_;
SET products;
IF type = "Single" THEN DO;
CALL execute('%split_prod('||price_floor||');');
END;
IF type = "Multi" THEN DO;
CALL execute('%select_prod('||price1||','||price2||');');
END;
run;
There isn't anything wrong with the code besides the RUN.
%global price1;
%global price2;
data products;
infile cards dlm=',';
input Type $ price_floor price_tier1 price_tier2;
cards;
Multi, 40, 20, 60
;;;;;
%macro container();
DATA _null_;
SET products;
IF type = "Single" THEN DO;
CALL SYMPUTX('price1', price_floor,'g');
END;
IF type = "Multi" THEN DO;
CALL SYMPUTX('price1', price_tier1,'g');
CALL SYMPUTX('price2', price_tier2,'g');
END;
RUN;
%mend;
%container();
%PUT &=price1;
%PUT &=price2;
LOG:
96 %PUT &=price1;
PRICE1=20
97 %PUT &=price2;
PRICE2=60
You never showed your call for %container() so not sure what the underlying issue is, but CALL EXECUTE is a better method as debugging macros is painful.
I have understood, and seen in other programs, that the following syntax is true.
%let variable = 'something';
statement name "&variable\othertext"; // something\othertext
However, in the code I have written I get this error message: Apparent symbolic reference not resolved. for the line LIBNAME REMOTE
%let month = 'JUN';
%let year = '18';
%let zos = ***********
signon zos ********************;
libname name "*********************************";
rsubmit;
libname remote "AAAA.BBBB.&month&year.SASLIB" access = readonly;
proc download inlib=remote outlib=name;
run;
libname remote clear;
endrsubmit;
signoff;
What am I missing?
The MONTH and YEAR macro variables are being defined in your local session, yet you're trying to resolve them in a remote session.
Use %SYSRPUT and %SYSLPUT to assign macro variables between sessions.
/* Local to remote */
%LET MONTH = 12 ;
%LET YEAR = 2018 ;
%SYSLPUT MONTH = &MONTH ;
%SYSLPUT YEAR = &YEAR ;
rsubmit ;
%PUT &MONTH &YEAR ;
/* resolves 12 and 2018 respectively */
/* remote to local */
%SYSRPUT FOO = BAR ;
endrsubmit ;
%PUT &FOO ; /* resolves to BAR */
More context would help, but most likely you are not understanding the role that period plays in resolving macro variable (symbol) references. To allow you to place letters and digits next to macro variable references SAS needs a way to tell where the name of the macro ends and the plain text starts. Period is used for that.
So if you wanted to generate this string
"AAAA.BBBB.JAN18.SASLIB"
from month and year values. First make sure to set the macro variables to the text you actually want. Quotes are just text to the macro processor.
%let month=JAN ;
%let year= 18;
Then in when you replace the values with macro variable references you will need an extra period after &YEAR so that one actually is generated. You should probably just get in the habit of always adding the period when referencing a macro variable.
"AAAA.BBBB.&month.&year..SASLIB"
I am trying to identify cases and controls in my dataset using a published SAS program. I am new to SAS and macros hope someone will be able to help troubleshoot. I am getting the following error
WARNING: Apparent symbolic reference VCNT not resolved.
Here is my code
data aggregate_all_11;
set aggregate_all_10 (rename=(age1=age));
run;
%LET AGERANGE=1;
%LET RATIO=5;
DATA CASES CONTROLS;
SET aggregate_all_11;
IF abuser = 1 THEN OUTPUT CASES;
ELSE OUTPUT CONTROLS;
PROC FREQ NOPRINT DATA=CASES;
TABLES age*gender/OUT=CASEOUT;
%MACRO SAMPLE(V_AGE,V_SEX,V_COUNT);
DATA QUALIFY1;
SET CONTROLS;
WHERE (&V_AGE-&AGERANGE <=AGE<=&V_AGE+&AGERANGE)
AND (GENDER = '&V_SEX');
CASE_AGE=&V_AGE;
CASE_SEX='&V_SEX';
SEED=RANUNI(0);
PROC SORT; BY SEED;
DATA QUALIFY2;
SET QUALIFY1 NOBS=TOTOBS;
IF _N_<=&V_COUNT*&RATIO;
IF &VCNT*&RATIO <= TOTOBS THEN TAG ='YES'; ELSE TAG ='NO';
PROC APPEND BASE=MATCHES DATA=QUALIFY2;
PROC SORT DATA=QUALIFY2 OUT=TEMP1(KEEP=UNIQUEID); BY UNIQUEID;
PROC SORT DATA=CONTROL OUT=TEMP2;BY UNIQUEID;
DATA CONTROL;
MERGE TEMP1(IN=IN1) TEMP2(IN=IN2);
BY UNIQUEID; IF IN2 AND NOT IN1;
%MEND SAMPLE;
DATA _NULL_; SET CASEOUT;
CALL EXECUTE('%SAMPLE('||AGE||','||GENDER||','||COUNT||')');
RUN;
LOG
NOTE: Line generated by the CALL EXECUTE routine.
1 +
DATA QUALIFY2; SET QUALIFY1 NOBS=TOTOBS; IF _N_<=5*5; IF &VCNT*5 <= TOTOBS THEN TAG ='YES'; ELSE
_
22
WARNING: Apparent symbolic reference VCNT not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant,
a missing value, INPUT, PUT.
2 + TAG ='NO';
NOTE: The SAS System stopped processing this step because of errors.
WARNING: The data set WORK.QUALIFY2 may be incomplete. When this step was stopped there were 0 observations and 46 variables.
WARNING: Data set WORK.QUALIFY2 was not replaced because this step was stopped.
NOTE: DATA statement used (Total process time):
I am stuck trying to iterate through a list of values in a macro %do loop. Each value is supposed to be used as a variable suffix.
The approach is based on SAS documentation: http://support.sas.com/kb/26/155.html
The (simplified) code is:
%macro loop(values);
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value=%qscan(values,i,%str(,));
proc sql;
select count(distinct hut_id) as prefix_&value.
from saslib.tl1_results_eval
group by plan_cell;
quit;
%end;
%mend;
%loop(%str(a,b,c,d))
The resulting error message is:
MLOGIC(LOOP): %DO loop beginning; index variable I; start value is 1; stop value is 4; by value
is 1.
MLOGIC(LOOP): %LET (variable name is VALUE)
MPRINT(LOOP): proc sql;
22: LINE and COLUMN cannot be determined.
NOTE 242-205: NOSPOOL is on. Rerunning with OPTION SPOOL might allow recovery of the LINE and
COLUMN where the error has occurred.
ERROR 22-322: Syntax error, expecting one of the following: ',', AS.
MPRINT(LOOP): select count(distinct hut_id) as prefix_a from group by plan_cell;
MPRINT(LOOP): quit;
Interestingly enough, if I remove "prefix_" from "count(distinct hut_id) as prefix_&value." - it works absolutely fine. Unfortunately, I do need the prefix there. Also, as you can see in the log message, the PROC SQL statement does get compiled correctly in the end, but the code errors out before getting executed.
Any ideas what's happening here? Thank you!
You are inserting invisible quoting characters due to the qscan function. Try:
%macro loop(values);
proc sql;
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value=%qscan(values,i,%str(,));
select count(distinct hut_id) as prefix_%unquote(&value)
from saslib.tl1_results_eval
group by plan_cell;
%end;
quit;
%mend;
%loop(%str(a,b,c,d))
Note the movement of the proc sql; statement - there are few good reasons to quit; and reinstantiate the SQL procedure between each SQL statement, which incurs overhead..
I have a block of code that connects to a remote server, passes a few variables to it, and then queries data on the server and then manipulates it. This code works exactly as I would expect it, producing the data I need.
However, I need to run the code in a macro loop. This is where it all breaks down. I'm not sure what the issue is, but I suspect it is some sort of variable scope problem. I have tried to research doing this online, but cannot figure it out.
The problem occurs in the data xtemp2 block. When I try to run this, I get the following errors:
WARNING: Apparent symbolic reference INTERVAL_SECONDS not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name,
a quoted string, a numeric constant, a datetime constant,
a missing value, INPUT, PUT.
and
WARNING: Apparent symbolic reference START_TIME not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name,
a quoted string, a numeric constant, a datetime constant,
a missing value, INPUT, PUT.
Please also note that I sometimes get similar errors with rtime, iprice, oprice, and itime. Once again, this code works perfectly well when I run it by itself. Putting it into a macro with a loop seems to generate these problems, which makes me think I am not initializing these variables properly. I would really appreciate any insight and tips you could provide.
%macro getthedata(nrows,ystart,yend); *nrows is the number of rows in the text file;
%do i=1 %to &nrows;
%do m=&ystart %to ¥d;
(...)
signon username=_prompt_;
%syslput VAR1 = &var1;
%syslput M = &m;
rsubmit;
libname abc'/data/sasdata'; *Thisis where the datasets are located;
%let start_time = '9:30:00't; * starting time;
%let interval_seconds =15*60; * interval is 15*60 seconds, 15min;
data all2009;
set sas.a_&M.01:;
by symbol date time;
where symbol = &VAR1 and time between '9:30:00't and '16:00:00't;
run;
data xtemp2;
set all2009;
by symbol date time;
format itime rtime time12.;
if first.symbol=1 or first.date=1 then do;
*Initialize time and price when new symbol or date starts;
rtime=time;
iprice=bid;
oprice=ofr;
itime=&start_time;
end;
if time >= itime then do; *Intervalreached;
output; *rtime and iprice hold the last observation values;
itime = itime +&interval_seconds;
do while(time >= itime); *need to fill in alltime intervals;
output;
itime = itime +&interval_seconds;
end;
end;
rtime=time;
iprice=bid;
oprice=ofr;
retain itime rtime iprice oprice; *Carry time and price valuesforward;
*keep symbol date itime iprice rtime;
run;
proc download data=all2009 out=local.all30 (keep=SYMBOL DATE PRICE SIZE itime);
run;
endrsubmit;
(...)
%end;
%end;
%mend getthedata;
Options MPRINT;
%getthedata(3,2007,2007)
SOLUTION (per Joe's Answer)
I was able to successfully create the interval_seconds and start_time variables using the %NRSTR solution Joe posted.
Here is the relevant modified code section:
(...)
signon username=_prompt_;
%syslput VAR1 = &var1;
%syslput M = &m;
rsubmit;
libname abc'/data/sasdata'; *Thisis where the datasets are located;
%nrstr(%%)let start_time = '9:30:00't; * CHANGED LINE;
%nrstr(%%)let interval_seconds =15*60; * CHANGED LINE;
data all2009;
set sas.a_&M.01:;
by symbol date time;
where symbol = &VAR1 and time between '9:30:00't and '16:00:00't;
run;
(...)
I can give you a functional solution, but I haven't figured out the whys yet.
Basically, the %let statements (and %put statements) are being ignored. They're not being passed down - in fact they're being executed on the local machine. See this:
%let var1="Region1";
signon;
libname uwork slibref=work server=unix;
data uwork.pricedata;
set sashelp.pricedata;
run;
%macro getthedata(nrows,ystart,yend); *nrows is the number of rows in the text file;
%do i=1 %to &nrows;
%do m=&ystart %to ¥d;
signon;
%syslput VAR1 = &var1;
%syslput ystart=&ystart;
%syslput yend=¥d;
%syslput start_time='01JAN1998'd;
%syslput interval_seconds=30;
rsubmit;
%*libname abc '/data/sasdata'; *Thisis where the datasets are located;
%let start_time = '01JAN1998'd; * starting time; *these are ignored by the rsubmit - why?;
%let interval_seconds =30; * interval is 15*60 seconds, 15min;
%put &start_time &interval_seconds;
data all2009;
set work.pricedata;
by date;
where year(date) ge &ystart. and year(date) le ¥d.;
run;
data xtemp2;
set all2009;
by date;
format itime rtime time12.;
if first.date=1 then do;
*Initialize time and price when new symbol or date starts;
rtime=date;
iprice=price;
oprice=cost;
itime=&start_time;
end;
if date >= itime then do; *Intervalreached;
output; *rtime and iprice hold the last observation values;
itime = itime +&interval_seconds;
do while(date >= itime); *need to fill in alltime intervals;
output;
itime = itime +&interval_seconds;
end;
end;
rtime=date;
iprice=price;
oprice=discount;
retain itime rtime iprice oprice; *Carry time and price valuesforward;
*keep symbol date itime iprice rtime;
run;
/*proc download data=all2009 out=local.all30 (keep=SYMBOL DATE PRICE SIZE itime);
run;
*/
endrsubmit;
%put &start_time &interval_seconds;
%end;
%end;
%mend getthedata;
Options MPRINT;
%getthedata(3,1998,1998)
Notice that the %put statement at the end, after the endrsubmit, actually works - even though it shouldn't (the macro variables shouldn't be defined on the local machine). There must be some issue with RSUBMITs inside of macros having macro variables, and there I have no real answers other than %SYSLPUTting it before you rsubmit (hence why my example works).
You could consider moving the RSUBMIT code to a remote batch program that you execute rather than RSUBMITting, perhaps, or even %include- maybe that would get around it (ie, inside the RSUBMIT have an %include that is referring to a remote program).
Take a look at this SAS article, which gives you some explanation of what's happening (basically what I said is in fact the case) and how to work around it. SYSLPUT is the first suggestion, and using other workarounds like %NRSTR also are possibilities.
The code looks like it should run, but I've tried something similar and had same problem.
You create local macro so the macro statements like LET are executed locally. While datastep statements are executed by remote SAS.
The solution is to define macro in remote session.
See effect of where is macro compiled below:
1) local macro:
%macro local_macro;
rsubmit;
%put %sysget(COMPUTERNAME);
endrsubmit;
%mend;
%local_macro
2) remote macro
rsubmit;
%macro remote_macro;
%put %sysget(COMPUTERNAME);
%mend;
endrsubmit;
rsubmit;
%remote_macro
;
endrsubmit;