Default catalog for stored macro SAS - sas

By default it is defined to store macros at WORK.SASMACR.
However at my site location for macros storage is different and I do not know where it is.
Can I find the default macros catalog and view what is inside?

Please see below effects of system options SASMSTORE + MSTORED and STORE option in macro definition on location of the compiled macros - could be something similar was used on your site.
option sasmstore=sasuser mstored;
%macro _mstore /store;
%put This is macro with mstore;
%mend;
%macro _nomstore;
%put This is macro without mstore;
%mend;
proc options option=sasmstore;
run;
Use DICTIONARY.CATALOGS to list macros compiled in your session.
proc sql;
create table macros as
select * from dictionary.catalogs where objtype='MACRO';
quit;

You have three option to find macros `
1.
proc catalog catalog=work.SASMACR;
contents;
run;
2.
proc catalog catalog=work.SASMAC1;
contents;
run;
3.
proc sql;
create table macros as
select * from dictionary.catalogs where objtype='MACRO';
quit;

Related

How to tell SAS to compile a Macro each time it is called instead of doing it manually

Context: We use SAS 9.4 and Enterprise Guide 7.15. Currently, we are implementing some new Macros and of course have to change a lot along the way. Sometimes smaller, sometimes bigger changes. The problem is, that in order to get the changes to work, SAS needs us to manually compile the Macro code or to restart the session which is a bit tedious.
This is the current setup in our main file (which calls all the Macros):
/* Macro options */
MAUTOSOURCE sasautos = "<path to macro>" mlogic mlogicnest mprint mprintnest MRECALL
Is it possible, while using the MAUTOSOURCE */ sasautos ="" option, to tell SAS every time the Macro is called to actually also compile the Macro instead of using the session-stored Macro? Ideally, the Macro would only be compiled when the whole line of code from the main file (MAUTOSOURCE */ sasautos ="" etc.) is executed, otherwise it should keep a compiled version in the session.
I found this paper (The Autocall Macro Facility in the SAS for Windows Environment) which states in the conclusion
After that SAS will use the code that has already
been compiled. If changes are made to the macro, it must be
compiled again before the changes go into effect.
which I hope doesn't mean that I have to do it manually. Is there any Macro option to set?
In SAS 9.3 they added the %SYSMACDELETE macro function. So if you just want to let autocall redefine a single macro then use that to remove the current definition.
%symacdelete mymacro;
Here is a utility macro that will query SASHELP.VCATALG view to find compiled macros in the WORK library and delete them. It has options to either list names of macros to delete or keep. Note that normal SAS sessions use WORK.SASMACR to store the compiled macros. But SAS/Studio and EG (and perhaps other ways of running SAS) use WORK.SASMAC1 instead.
https://github.com/sasutils/macros/blob/master/macdelete.sas
%macro macdelete(delete,keep);
/*----------------------------------------------------------------------------
Remove compiled macros using %SYSMACDELETE macro statement.
Use DELETE parameter to list macro names to delete.
Use KEEP parameter to list macro names to NOT delete.
Calling it with no values will delete all macros not currently running.
----------------------------------------------------------------------------*/
%local libname memname objname objtype fid i;
%do i=1 %to %sysmexecdepth;
%let keep=%sysmexecname(&i) &keep;
%end;
%if %length(&delete) %then %let delete=and findw("&delete",objname,',','sit');
%let fid=%sysfunc(open( sashelp.vcatalg(keep=libname memname objname objtype
where=(libname='WORK' and objtype='MACRO' and memname like 'SASMAC_'
and not findw("&keep",objname,',','sit') &delete))));
%if (&fid) %then %do;
%syscall set(fid);
%do %while(0=%sysfunc(fetch(&fid)));
%put %sysfunc(compbl(Removing &objname from &libname catalog &memname));
%sysmacdelete &objname;
%end;
%let fid=%sysfunc(close(&fid));
%end;
%else %put %qsysfunc(sysmsg());
%mend macdelete;
Example:
3348 %macro test1; %mend;
3349 %macro test2; %mend;
3350 %macro test3; %mend;
3351 %macro test4; %mend;
3352 %macdelete(test1 test3);
Removing TEST1 from WORK catalog SASMACR
Removing TEST3 from WORK catalog SASMACR
3353 %macdelete(keep=test2);
Removing TEST4 from WORK catalog SASMACR
Example when running SAS/Studio or Enterprise Guide:
97 %macro test1; %mend;
98 %macro test2; %mend;
99 %macro test3; %mend;
100 %macro test4; %mend;
101 %macdelete(test1 test3);
Removing TEST1 from WORK catalog SASMAC1
Removing TEST3 from WORK catalog SASMAC1
If you delete the compiled macro from WORK.SASMACR SAS will have to re-compile the macro when you call it again.
proc catalog c=work.sasmacr;
*contents;
delete your-macro-to-recompile.macro;
run;
quit;
For your normal users you should set a release schedule of changes. The users will know they need to re-start a new session after the release is made.
For your developers that are testing the changes as they are made they just need to use %INCLUDE to re-compile the macro. So if you know that macro XYZ is changed then just run:
%include maclib('xyz.sas');
Or you could brute force it and recompile all of the macros in your autocall library.
%incldue maclib('*.sas');
You could get fancier and make a macro that cleans out the actual catalog of compiled macros. Something like:
%macro clean_autocall;
proc catalog force c=work.sasmacr;
save clean_autocall /et=macro;
quit;
options mrecall mautosource;
%mend clean_autocall;
But if you are using Enterprise Guide there are two issues.
First for some reason it uses a different catalog to store the compiled macros. (Why?) I think it is WORK.SASMAC1 instead of WORK.SASMACR.
Second EG will manually compile a bunch of helper macros that it needs. I am not sure if there is an official source for the complete list of these macros? You could try adding code to your project to automatically create the list based on what entries are in the catalog when your project starts. Here is a list I made 10+ years ago when I tried using EG with production environment. But I am sure that it is out of date.
%let s_eg_save= checkfmt checkhotfix
eclibassign eclibunassign enterpriseguide gaccessible
_eg_conditional_dropds
;

Can I use wildcards in dataset names for PROC CONTENTS?

On the SAS server we have a library that contains thousands of datasets. I want to catalog the contents of a subset of these, all of which have names that begin with "prov". Can I use a wildcard to specify this?
I tried:
PROC CONTENTS DATA=library.prov*;
RUN;
But that just produces a log with this error message:
ERROR: File LIBRARY.PROV.DATA does not exist.
I also tried library.prov%, and that gave the same error.
There are over 100 datasets that start with "prov" so I really don't want to have to do them one at a time. Any ideas?
Depending on what information you want that the CONTENTS procedure produces you could just use the DICTIONARY metadata views.
proc sql ;
create table want as
select *
from dictionary.columns
where libname = 'LIBREF'
and memname like 'PROV%'
;
quit;
Use a WHERE data set option.
proc contents data=sashelp._all_ noprint out=class(where=(memname like 'CLASS%'));
run;
When you specify the keyword _ALL_ in the PROC CONTENTS statement, the step displays a list of all the SAS files that are in the specified SAS library.
Example :
PROC CONTENTS DATA=libref._ALL_ NODS;
RUN;
But to open only the datasets that begin with prov you can use the SQL and add CONTAINS to WHERE e.g:
proc sql ;
create table mytables as
select *
from dictionary.tables
where libname = 'WORK'
order by memname ;
quit ;
Now just run:
PROC CONTENTS DATA mytables;
RUN;
I may be using a different version of SAS check if you have the library SASHELP if so try this based on my note in your comment on the previous response you may see that this works out for you:
proc sql outobs=100;
create table see as
select distinct libname,memname,crdate,modate from sashelp.vtable
where libname='LIBRARY' and memname like 'PROV%'
order by memname;
quit;

Passing macro function as argument to another macro function

I am new to SAS and I am playing around with macros.
I want to define a function returning a truth value and pass it as argument to another function where it is used in an %IF. As an example (not working):
%MACRO truth(datum);
%PUT datum < 100;
%MEND;
%MACRO use_truth(table, condition);
%PROC SQL;
SELECT *
FROM table
WHERE %condition(number);
QUIT;
%MEND
%use_truth(table1 , truth) ;
Where table1 is an SQL table with some column number.
How can I make such a procedure work?
One important thing to remember is that macros are just text substitution, and they're run before any of your code executes. Macros don't return values as such, they just return code.
When you run WHERE %&condition(number); (note the ampersand to actually resolve the value of the macro variable) what the proc SQL actually ends up seeing is WHERE %PUT number < 100;
Here's how you could do what you'd like, but be aware that the 'truth' macro isn't actually returning a yes/no value; it's just writing that text into the proc SQL.
%MACRO truth(datum);
&datum < 100
%MEND;
%MACRO use_truth(table, condition);
PROC SQL;
SELECT *
FROM &table
WHERE %&condition(number);
QUIT;
%MEND;
%use_truth(testInput, truth);
If you're dealing with more complicated logic, I'd suggest looking into proc FCMP. That'll let you define custom functions that can be used in data steps and proc SQL.
A macro that is generating code inline to be used as part of another statement can not itself cause any sort of step to occur (Proc or Data)
You can pass a macro name to another macro, and invoke it after resolving it.
%macro dispatch (macro_name);
%&macro_name
%mend;
%macro a;
%put NOTE: Aaaaaaay!;
%mend;
%dispatch (a)

declare variabe in PROC SQL (SAS)

I am trying to use a variable on PROC SQL but i cannot find a proper way through the internet. I just want to apply the following code of T-SQL on PROC SQL:
declare #example as int;
set #example=2;
select * from {table} where {column}=#example;
go
How can i apply this code on PROC SQL?
The translation to SAS SQL is to use a macro variable, the code looks pretty similar, need to wrap it in a PROC SQL block though.
%let example=2;
proc sql;
select *
from table
where variable=&example;
quit;
EDIT: my original reference to the macro variable was incorrect, use an ampersand in SAS not # symbol.

proc sql outobs= triggers SAS warning

We currently use the %runquit macro function as detailed here (http://analytics.ncsu.edu/sesug/2010/CC07.Blanchette.pdf). The %runquit macro is shown below. It basically stops running any more SAS code when an error is encounterd, and can be used as a replacement for both the run and quit statements:
%macro runquit;
; run; quit;
%if &syserr %then %abort cancel;
%mend;
Because using the outobs statement in proc sql triggers a system error (even when the nowarn option is specified) it means we are unable to use the %runquit macro when we need to use the outobs= option.
The below example will generate the following warning message:
proc sql noprint outobs=3 /*nowarn*/;
create table tmp as
select age, count(*) as freq
from sashelp.class
group by 1
order by 2 desc
;
%runquit;
WARNING: Statement terminated early due to OUTOBS=3 option.
Thank you SAS for the completely unnecessary warning. The behaviour is obviously expected because I explicitly wrote code to ask for it. I don't see warnings given when we specify inobs= and outobs= on a set statement. Why does proc sql get the special treatment?
Is there any way to disable the warning issues by the outobs= option in proc sql? Alternatively, is there another way to limit the output rows from proc sql that will not generate an error?
Assuming you are okay with the full SQL statement executing, you can get around this with a data step view that contains the obs limitation.
proc sql noprint ;
create table tmp as
select age, count(*) as freq
from sashelp.class
group by 1
order by 2 desc
;
%runquit;
data tmp_fin/view=tmp_fin;
set tmp(obs=3);
%runquit;
Or make the SQL statement a view and use the data step to make the data set.
proc sql noprint ;
create view tmp_view as
select age
, count(*) as freq
from sashelp.class
group by 1
order by 2 desc
;
quit;
data tmp;
set tmp_view(obs=3) ;
run;
This might be one of your options considering I/O is not a huge constraint, here the reset outobs= option with nowarn does the trick but at IOs cost.
proc sql;
create table test as
select * from sashelp.class;
reset outobs=10 nowarn;
create table test1 as
select * from sashelp.class;
quit;