SAS: %PUT doesn't show macro variable in the log - sas

I have the following code:
data name_list;
length name $10;
input name $;
datalines;
Peter
John
Paul
David
;
run;
proc sql ;
select name
into :names separated by '" "'
from name_list
where substr(name,1,1) = 'P'
;
quit;
%put names;
The code work without any error and it does show the two names starting with P, however in the log I can't see the result of the %put statement.
After the execution of the PROC SQL I have the following log:
35 quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
36 %put names;
names
Why the names stored into the macro variable is not printed?

In your code you %put string that equals "names".
If you want to %put macro variable &names:
%let names=1;/*initializing macro variable*/
%put &=names;
names=1;
%put &names;
1

Related

Derive unique libref and/or fileref in SAS

I often need to assign (and de-assign) temporary librefs/filerefs as part of utility macros - and thus I need to avoid naming conflicts with any existing librefs/filerefs. I know I could query the sashelp or dictionary tables and use conditional logic to iterate until I find one that isn't assigned, but I wondered if there was some easier way?
For instance, the following will create a uniquely named dataset in the work library:
data;run;
Is there some equivalent for librefs / filerefs?
The FILENAME() function already provides a method for this. When you call it with a missing value for the fileref it generates one using format #LNnnnnn.
6 data test;
7 length fileref $8 ;
8 rc=filename(fileref,,'temp');
9 put rc= fileref=;
10 run;
rc=0 fileref=#LN00056
NOTE: The data set WORK.TEST has 1 observations and 2 variables.
NOTE: DATA statement used (Total process time):
real time 0.02 seconds
cpu time 0.01 seconds
11 %global fileref ;
12 %let rc=%sysfunc(filename(fileref,,temp));
13 %put &=rc &=fileref ;
RC=0 FILEREF=#LN00058
The undocumented monotonic function, invoked in the open execution space, is very handy for obtaining an unused value.
libname mylib "C:\temp\sandbox";
data mylib.data%sysfunc(monotonic());
…
run;
Or code a macro to deliver a name for a libref. The macro can also check for existence if so desired:
%macro nextName(lib=,base=data,check=1);
%local index name;
%if %length(&lib) %then %let lib=&lib..;/* handle non-empty lib */
%do %until (&check and not %sysfunc(exist(&name,data)));
%let name = &lib.&base.%sysfunc(monotonic());
%end;
&name
%mend;
data data3;run;
data data4;run;
%put %nextName();
%put %nextName();
%put %nextName();
%put %nextName();
proc sort data=sashelp.class out=%nextname();
by age;
run;
You could go robust macro implementation and test for lib existence and valid check value.
The LIBNAME function has a similar feature as FILENAME but it does not populate the reference name variable as with FILENAME. The only way I can think of would be to compare SASHELP.VLIBNAM before and after. The librefs are in the form WC00000n.
79 data _null_;
80 length libref $32.;
81 libref = ' ';
82 rc = libname(libref,'.');
83 msg = sysmsg();
84 put _all_;
85 run;
libref= rc=-70004 msg=NOTE: Libref refers to the same physical library as WC000004. _ERROR_=0 _N_=1
actually writing one was fairly trivial, and didn't involve querying the (expensive) dictionary table:
libname mcore0 (work);
libname mcore1 (work);
libname mcore2 (work);
%macro mf_getuniquelibref(prefix=mcore,maxtries=1000);
%local x;
%let x=0;
%do x=0 %to &maxtries;
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
%put Libref &prefix&x is available!;
&prefix&x
%return;
%end;
%end;
%put unable to find available libref in range &prefix.0-&maxtries;
%mend;
%let libref=%mf_getuniquelibref();
%put &=libref;
which returns;
UPDATE:
I've added macros for both of these to the MacroCore library and they can be utilised as follows:
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/macrocore.sas";
%inc mc;
%let libref=%mf_getuniquelibref();
%let fileref=%mf_getuniquefileref();

SAS macro - rename variables using their label values as their new variable names

I want to produce a macro that converts the variable names of a dataset into the variables' labels. I intend to apply this macro for large datasets where manually changing the variable names would be impractical.
I came across this code online from the SAS website, which looked promising but produced errors. I made slight edits to remove some errors. It now works for their sample dataset but not mine. Any assistance with improving this code to work with my sample dataset would be greatly appreciated!
SAS sample dataset (works with code):
data t1;
label x='this_x' y='that_y';
do x=1,2;
do y=3,4;
z=100;
output;
end;
end;
run;
My sample dataset (does not work with code):
data t1;
input number group;
label number = number_lab group = group_lab;
datalines;
1 1
1 .
2 1
2 .
3 2
3 .
4 1
4 .
5 2
5 .
6 1
6 .
;
run;
Code:
%macro chge(dsn);
%let dsid=%sysfunc(open(&dsn));
%let cnt=%sysfunc(attrn(&dsid,nvars));
%do i= 1 %to &cnt;
%let var&i=%sysfunc(varname(&dsid,&i));
%let lab&i=%sysfunc(varlabel(&dsid,&i));
%if lab&i = %then %let lab&i=&&var&i;
%end;
%let rc=%sysfunc(close(&dsid));
proc datasets;
modify &dsn;
rename
%do j = 1 %to &cnt;
%if &&var&j ne &&lab&j %then %do;
&&var&j=&&lab&j
%end;
%end;
quit;
run;
%mend chge;
%chge(t1)
proc contents;
run;
My code produces the following error messages:
ERROR 73-322: Expecting an =.
ERROR 76-322: Syntax error, statement will be ignored.
Mainly you are not closing the RENAME statement with a semi-colon. But it also looks like you have the RUN and QUIT statements in the wrong order.
But note that there is no need for that complex %sysfunc() macro code to get the list of names and labels. Since you are already generating a PROC DATASETS step your macro can generate other SAS code also. Then your macro will be clearer and easier to debug.
%macro chge(dsn);
%local rename ;
proc contents data=&dsn noprint out=__cont; run;
proc sql noprint ;
select catx('=',nliteral(name),nliteral(label))
into :rename separated by ' '
from __cont
where name ne label and not missing(label)
;
quit;
%if (&sqlobs) %then %do;
proc datasets nolist;
modify &dsn;
rename &rename ;
run;
quit;
%end;
%mend chge;
If the list of rename pairs is too long to fit into a single macro variable then you could resort to generating two series of macro variables using PROC SQL and then add back your %DO loop.
Here is SAS log from testing on your sample file.
4156 %chge(t1);
MPRINT(CHGE): proc contents data=t1 noprint out=__cont;
MPRINT(CHGE): run;
NOTE: The data set WORK.__CONT has 2 observations and 41 variables.
NOTE: PROCEDURE CONTENTS used (Total process time):
real time 0.08 seconds
cpu time 0.01 seconds
MPRINT(CHGE): proc sql noprint ;
MPRINT(CHGE): select catx('=',nliteral(name),nliteral(label)) into :rename
separated by ' ' from __cont where name ne label and not missing(label) ;
MPRINT(CHGE): quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.04 seconds
cpu time 0.00 seconds
MPRINT(CHGE): proc datasets nolist;
MPRINT(CHGE): modify t1;
MPRINT(CHGE): rename group=group_lab number=number_lab ;
NOTE: Renaming variable group to group_lab.
NOTE: Renaming variable number to number_lab.
MPRINT(CHGE): run;
NOTE: MODIFY was successful for WORK.T1.DATA.
MPRINT(CHGE): quit;
NOTE: PROCEDURE DATASETS used (Total process time):
real time 0.12 seconds
cpu time 0.00 seconds
Note that if I now try to run it again on the modified dataset it does not rename anything.
4157 %chge(t1);
MPRINT(CHGE): proc contents data=t1 noprint out=__cont;
MPRINT(CHGE): run;
NOTE: The data set WORK.__CONT has 2 observations and 41 variables.
NOTE: PROCEDURE CONTENTS used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
MPRINT(CHGE): proc sql noprint ;
MPRINT(CHGE): select catx('=',nliteral(name),nliteral(label)) into :rename
separated by ' ' from __cont where name ne label and not missing(label) ;
NOTE: No rows were selected.
MPRINT(CHGE): quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.08 seconds
cpu time 0.00 seconds
you're missing a semi-colon here:
&&var&j=&&lab&j
if you still get an error, turn on these options and let us know where the error occurs.
options symbolgen mprint;

PROC SQL "SELECT INTO" does not create macro variable

I've run into an issue creating macro variables using proc sql select into : logic. The code seems to be pretty straightforward, but I should note that the macro that is causing problems is called from several other macros.
Here is the snippet that is causing issues.
%do year_num=1 %to 5;
%if &year_num=1 %then %let min_date = %eval(&max2.-17);
%else %let min_date = %eval(&min_date.-12);
data tmp;
set bf(firstobs=&min_date obs=%eval(11+&min_date));
run;
data tmp2;
set bf(firstobs=%eval(5+&min_date) obs=%eval(7+&min_date));
run;
proc sql noprint;
select sum(EP), sum(ExpectedLoss)
into :totep, :totexpt
from tmp;
select sum(EP), sum(ExpectedLoss)
into :partep, :partexpt
from tmp2;
quit;
%put _LOCAL_;
*Other code...;
%end;
For some reason, the variables totep, totexpt, partep and pqrtexpt are not getting created and I can't find any useful information in the log that may shed light on the situation.
Here is part of the log, including the output from _LOCAL_.
SYMBOLGEN: Macro variable YEAR_NUM resolves to 1
SYMBOLGEN: Macro variable MAX2 resolves to 96
MPRINT(BFMETHOD): data tmp;
SYMBOLGEN: Macro variable MIN_DATE resolves to 79
SYMBOLGEN: Macro variable MIN_DATE resolves to 79
MPRINT(BFMETHOD): set bf(firstobs=79 obs=90);
MPRINT(BFMETHOD): run;
MPRINT(BFMETHOD): data tmp2;
SYMBOLGEN: Macro variable MIN_DATE resolves to 79
SYMBOLGEN: Macro variable MIN_DATE resolves to 79
MPRINT(BFMETHOD): set bf(firstobs=84 obs=86);
MPRINT(BFMETHOD): run;
MPRINT(BFMETHOD): proc sql noprint;
MPRINT(BFMETHOD): select sum(EP), sum(ExpectedLoss) into :totep, :totexpt from tmp;
MPRINT(BFMETHOD): select sum(EP), sum(ExpectedLoss) into :partep, :partexpt from tmp2;
MPRINT(BFMETHOD): quit;
BFMETHOD I 12
BFMETHOD DSET all
BFMETHOD SEAS_MIN 0.6
BFMETHOD YEAR_NUM 1
BFMETHOD SEAS_MAX 1.66666666666666
BFMETHOD MIN_DATE 79
data tmp; set bf(firstobs=79 obs=90); run;
NOTE: There were 12 observations read from the data set WORK.BF.
NOTE: The data set WORK.TMP has 12 observations and 35 variables.
NOTE: Compressing data set WORK.TMP increased size by 100.00 percent.
Compressed is 2 pages; un-compressed would require 1 pages.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
109 + data tmp2; set bf(firstobs=84 obs=86); run;
NOTE: There were 3 observations read from the data set WORK.BF.
NOTE: The data set WORK.TMP2 has 3 observations and 35 variables.
NOTE: Compressing data set WORK.TMP2 increased size by 100.00 percent.
Compressed is 2 pages; un-compressed would require 1 pages.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
109 + proc sql noprint;
109 + select sum(EP), sum(ExpectedLoss) into :totep, :totexpt from tmp;
109 +
select sum(EP), sum(ExpectedLoss) into :partep, :partexpt from tmp2;
109 +
quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
If I remove noprint from the proc sql statement then the correct values are output so I'm not sure what's going on. Any help would be appreciated.
Your issue is almost certainly related to execution timing. Particularly when using call execute, you can easily run into problems with timing where the macro variable is resolved by the SAS processor earlier than it's given a useful value.
Here's a simplified example. I specifically make a dataset so you can see when that happens (it's not a necessary step nor does it affect the example, it just makes a useful log entry).
Notice how when the macro runs on its own, everything works how you'd expect; but when it's run with call execute the %put executes before the macro actually executes.
The third example uses %nrstr to force SAS to not try to resolve the macro before actually running it - which causes it to be submitted properly.
Effectively, the first call execute version has SAS process the macro text then submit it to sas.exe - which you do not really want. Adding %nrstr fixes that.
%macro do_something();
%local mlist;
data class_m;
set sashelp.class;
where sex='M';
run;
proc sql;
select name into :mlist separated by ' '
from class_m;
quit;
%put &=mlist;
%mend do_something;
%put Macro run on its own;
%do_something;
%put Macro run via call execute;
options mprint symbolgen;
data _null_;
set sashelp.class;
if _n_=1 then call execute('%do_something()');
stop;
run;
%put Macro run with nrstr and call execute;
data _null_;
set sashelp.class;
if _n_=1 then call execute('%nrstr(%do_something())');
stop;
run;

ERROR 180-322: Statement is not valid or it is used out of proper

I have searched some info about this error,but it seems none match mine,may someone familiar with this error take a look.
"Code generated by a SAS macro, or submitted with a "submit selected" operation in your editor, can leave off a semicolon inadvertently." it is still abstruse for me to explore in my code by this comment.although I got this error,the outcomes is right.may someone give any advice..thanks!
%let cnt=500;
%let dataset=fund_sellList;
%let sclj='~/task_out_szfh/fundSale/';
%let wjm='sz_fundSale_';
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
%mend write_dx;
%write_dx();
and if I am not using macro,there is no error.
data _null_;
options spool;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
--------------------------------update----------------------------------
I add % to the keyword,but still get the same error
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
%if _n_=1 %then i=cnt;
%if _n_<=i %then %do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
%end;
run;
%mend write_dx;
%write_dx();
Why did you add () to the macro call when the macro is not designed to accept any parameters? If you do that then the () are NOT processed by the macro processor and so are passed along to SAS to interpret. That is the same error message as you would get if you submitted (); by itself.
1 %macro xx ;
2 data _null_;
3 put 'Running data step in macro';
4 run;
5 %mend xx;
6 %xx();
Running data step in macro
NOTE: DATA statement used (Total process time):
real time 0.02 seconds
cpu time 0.00 seconds
6 %xx();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
7 ****;
8 ();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
But if you define it with 0 or more parameters.
%macro param();
generated code
%mend ;
%put |%param()|;
The macro processor will use the () and so they are not passed onto SAS to use.
|generated code|
Change
%macro write_dx;
to
%macro write_dx();
When creating a macro, you have to include the () even if no values are passed.

SAS: create a Macro that add suffix to variables in a dataset

I would like to create a macro that add a suffix to variable names in a dataset. below is my code:
%macro add_suffix(library=,dataset=,suffix=);
proc sql noprint;
select cat(name, ' = ', cats('&suffix.',name )) into :rename_list separated by ' ' from
dictionary.columns where libname = '&library.' and memname= '&dataset.';
quit;
proc datasets library=&library nolist nodetails;
modify &dataset;
rename &rename_list;
run;
quit;
%mend;
%add_suffix(library=OUTPUT,dataset=CA_SPREADS,suffix=CA);
It gives error messages:
NOTE: No rows were selected.
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
WARNING: Apparent symbolic reference RENAME_LIST not resolved.
NOTE: Line generated by the invoked macro "ADD_SUFFIX".
2 rename &rename_list; run;
-
22
76
NOTE: Enter RUN; to continue or QUIT; to end the procedure.
ERROR 22-322: Expecting a name.
ERROR 76-322: Syntax error, statement will be ignored.
If I put the library and dataset names in quotation mark, it works for the first block i.e. add values to string rename_list but not for the proc dataset step
Macro triggers like % and & are not honored inside of single quotes. That is why you are not getting any hits on your SQL query. There is no library name that has an & as the first character.
The reason it looked like it was sort of working is that when you use this in your SQL statement
catx('=',name,cats('&prefix.',name))
then you end up with a string like
age=&prefix.age
And that will actually work because the reference to the macro variable PREFIX will resolve when you run the RENAME statement.
You should just use double quotes instead.
%macro change_names(library=,dataset=,prefix=,suffix=);
%local rename_list;
proc sql noprint;
select catx('=',name,cats("&prefix",name,"&suffix"))
into :rename_list separated by ' '
from dictionary.columns
where libname = %upcase("&library")
and memname = %upcase("&dataset")
;
quit;
%if (&sqlobs) %then %do;
proc datasets library=&library nolist nodetails;
modify &dataset;
rename &rename_list;
run;
quit;
%end;
%else %put WARNING: Did not find any variables for &library..&dataset..;
%mend change_names;
%change_names(library=OUTPUT,dataset=CA_SPREADS,prefix=CA);
Your macro variables are not being resolved because you're wrapping them in single quotes ' rather than double quotes ".
You should uppercase the libname and memname parameters of your macro as these are always in uppercase in dictionary.columns.
Tested and works. Longer but maybe more beginner friendly approach. Input the dataset name and what suffix you want to add.
example: %add_suffix(orders, _old); /* will add _old suffix to all variables.*/
%macro Add_Suffix(Dataset, suffix);
proc contents noprint
data=work.&dataset out=sjm_tmp(keep=NAME);
run;
data sjm_tmp2;
set sjm_tmp;
foobar=cats(name, '=',NAME,'&suffix.');
run;
proc sql noprint;
select foobar into :sjm_list separated by ' ' from sjm_tmp2;
quit;
proc datasets library = work nolist;
modify &dataset;
rename &sjm_list;
quit;
proc datasets library=work noprint;
delete sjm_tmp sjm_tmp2 ;
run;
%mend Add_Suffix;