BookmarkSubscribeRSS Feed
deleted_user
Not applicable
Hi,

I was reading a book about macros and came across this:

You can assign macro variable values to other macro variables and combine macro variables in a %LET statement to create a new macro variable.

I am not clear about this. Can someone explain it with a simple example and tell me when we need to resort to this method / its advantage?

Thanks

Cathy
7 REPLIES 7
deleted_user
Not applicable
%let a = first;
%let b = last;
%let &a._and_&b = next;

&put first_and_last = &first_and_last;

I have used it to dynamically create a variable -- note _&dummy in the following code:
[pre]
%macro getPWD(user,dummy);
%put RP Note: inside macro getpwd(user,dummy) ;
%put - parameter user = &user ;
%put - parameter dummy = &dummy ;

%local dsid rc user_id varN _&dummy;
%let dsid=%sysfunc(open(misc.users(where=(user_id="&user")),i));
%if (&dsid > 0) %then %do;
%let varN = %sysfunc(varnum(&dsid,password));
%let rc=%sysfunc(fetchobs(&dsid,1));
%if (&rc = 0) %then %do;
%let _&dummy = %sysfunc(getvarc(&dsid,&varN));
%if %length(&dummy) > 0 %then %let &dummy = &&&&_&dummy ;
%else &&&&_&dummy ;
%end;
%else %put RP ERROR: failed to retrieve an observation from misc.users for user_id=&user ;
%let rc=%sysfunc(close(&dsid));
%end;
%else %put RP ERROR: failed to open misc.users ;

%put RP Note: finished getpwd ;
%mend;
[/pre]

dummy is an external macro variable that was supplied to hold the returned value of the macro (a user's password). This was done to support a bunch of legacy code that made the call:

[pre]
%local password;
%let user_id = %getID;
%getPWD(&user_id,password);
...
[/pre]

The original getPWD retrieved the password from a file in such a way that the following code would not work:
[pre]
libname my_oracle oracle user="%getID" password="%getPWD(%getID)" ... ;
[/pre]
but the new code does.
So, both the old method (legacy code) is not broken, and the new method is simpler to use.
Dwight
Calcite | Level 5
Chuck,

I enjoyed your post and your well-crafted macro code. I, too, strive to minimize maintenance disruption by honoring legacy interfaces while providing additional function without muddling the code.

Having an interest in macros myself, I shanghaied your code into my SAS laboratory to see how it works, especially that impressive quadruple ampersand piece! In my experience, I have never needed more than triple ampersands to produce the required indirection. More than triple ampersands seem to me to obscure the logic. So I wanted to see why you included a quad!

As I studied your code in the following test bed, it appeared to me that I might be able to simplify the 2 quads to doubles and yield the same result. When I attempted it, it seemed to work as the original. What are your thoughts?

OPTIONS MPRINT MPRINTNEST MLOGIC;

DATA work.users;
INPUT user_id : $8.
password : $25.
;

DATALINES;
z123456 bisko_bosco34
wakkaw yadda_yadda2314
abcdefg blini_wonker
;
RUN;

%macro getPWD(user,dummy);
%put RP Note: inside macro getpwd(user,dummy) ;
%put - parameter user = &user ;
%put - parameter dummy = &dummy ;

%local dsid rc user_id varN _&dummy;
%let dsid=%sysfunc(open(work.users(where=(user_id="&user")),i));
%if (&dsid > 0) %then %do;
%let varN = %sysfunc(varnum(&dsid,password));
%let rc=%sysfunc(fetchobs(&dsid,1));
%if (&rc = 0) %then %do;
%let _&dummy = %sysfunc(getvarc(&dsid,&varN));
%if %length(&dummy) > 0 %then %let &dummy = &&_&dummy ;
%else &&_&dummy ;
%end;
%else %put RP ERROR: failed to retrieve an observation from misc.users for user_id=&user ;
%let rc=%sysfunc(close(&dsid));
%end;
%else %put RP ERROR: failed to open misc.users ;

%put RP Note: finished getpwd ;
%mend;

%macro test;
%local password;
%let user_id = wakkaw;
%getPWD(&user_id,password)
%put _LOCAL_;
%mend;

%test

%PUT password=%getPWD(wakkaw);
deleted_user
Not applicable
I found the idea of using 4x& from a posting on a different website for something that I was looking for and can't remember what it was, nor where the site was, etc.

What I do remember is that I tried &, &&, &&&, and &&&&, and found that I really did need all four &'s for the macro to work properly in all cases.

case 1 = %local password; %getPWD(&userID,password);
case 2 = %global password; %getPWD(&userID,password);
case 3 = %getPWD(%getID)

cases 4+
OleDB.sas in MacLib -- which is part of "options sasautos=(MacLib SASAUTOS)"
%macro OleDB;
datasource='dns_name' provider=sqloledb user="&user" password="%getPWD(&user)" schema=dbo prompt=no
%mend;

OleDBlib.sas in IncludeLib
Libname my_db OleDB %OleDB;

then inside other SAS code
[pre]
proc sql;
connect to oledb (%OleDB);
...
disconnect from oledb;
quit;
[/pre]
and
[pre]
%include IncludeLib(OleDBlib);
[/pre]

and other similar kinds of things. Basically all the previously existing code, or at least representative samples.
Cynthia_sas
SAS Super FREQ
Hi:
While the other examples are truly magnificent examples, here's something a bit simpler (I hope):
[pre]
** define a generic macro program for sashelp.class;
%macro getobs;

** get rid of periods in WANTVAL for filename;
%let x = %sysfunc(translate(&wantval,'_','.'));
%let outfile = Apr08_&wantvar.&x;

%let wantvar = %upcase(&wantvar);

title "Report for &wantvar = &wantval";
title2 "Result File is: &outfile..html";

%if &wantvar = AGE or &wantvar = HEIGHT or
&wantvar = WEIGHT %then %do;
%let whrcls = where &wantvar EQ &wantval;
%end;
%else %if &wantvar = NAME or &wantvar = SEX %then %do;
%let whrcls = where &wantvar EQ "&wantval";
%end;
%else %do;
%let whrcls = %str(where 1);
title 'You did not specify name, age, height, weight or sex';
title2 'for &wantvar. check your program and submit again';
title3 'Printing all OBS for SASHELP.CLASS';
%end;

options mprint symbolgen;
ods html file="c:\temp\&outfile..html" style=sasweb;
proc print data=sashelp.class;
&whrcls;
run;
ods html close;
title; footnote;
options nomprint nosymbolgen;
%mend getobs;

[/pre]

Basically, this is a definition for a macro program. I am using it to build code for me so that, when the macro program is invoked, the generated code (when executed) will select observations out of SASHELP.CLASS. The macro variables that I will provide are &WANTVAR (for which variable they want -- name, age, height, weight, sex) and &WANTVAL will be the VALUE that somebody is asking for. My condition is always an EQ condition in the WHERE clause. But for every time I run the Macro program (%getobs), I want a separate file name to be generated that looks like this: Apr08_xxxxxxxyyyyy.html where xxxxx is the value for &WANTVAR and yyyyy is the value for &WANTVAL. AND, in order to build the correct WHERE clause, I have to know whether they asked for AGE, HEIGHT and WEIGHT for &WANTVAR or whether they asked for NAME or SEX -- because the WHERE clause would look different for character variables versus numeric variables. AND, since height and weight could have decimal points in the number, I need the decimal in the &WANTVAL macro variable, but I do NOT want the . in my file name.

So, the %let for X is getting rid of the . out of &WANTVAL and the %let for OUTFILE is building the special file name that I want. And, in addition to that, the %let for WHRCLS is being set conditionally depending on what they asked for. So, from 2 pieces of information provided to my macro program, I have done these OTHER things, just by creating new macro variables:
1) generated a file name for my HTML file based on the values they sent me (including stripping out the . and replacing it with _)
2) generated the correct where clause for character vs numeric variables


Then when I invoke that macro program, I will get the desired results for the first 4 invocations and all the obs in SASHELP.CLASS as a result of the last invocation. I'm not going to show all the output below, but I will show all of the TITLES from each run:
[pre]

** now use the macro program multiple times;
** note in the log how the where statement and output file;
** name get changed appropriately depending on wantvar and wantval;
%let wantvar = age;
%let wantval = 13;
%getobs;

Title Results:

Report for AGE = 13
Result File is: Apr08_age13.html

*************************************

%let wantvar = height;
%let wantval = 62.5;
%getobs;

Title Results:

Report for HEIGHT = 62.5
Result File is: Apr08_height62_5.html

*************************************

%let wantvar = name;
%let wantval = Alfred;
%getobs;

Title Results:

Report for NAME = Alfred
Result File is: Apr08_nameAlfred.html

*************************************

%let wantvar = sex;
%let wantval = F;
%getobs;

Title Results:

Report for SEX = F
Result File is: Apr08_sexF.html

*************************************


%let wantvar = Region;
%let wantval = Asia;
%getobs;

Title Results:

You did not specify name, age, height, weight or sex
for &wantvar. check your program and submit again
Printing all OBS for SASHELP.CLASS

*************************************

[/pre]

This is just ONE example of how you can build new macro variables from existing macro variables. There are many, many more. Generally, you use this feature of the macro facility because you need something in your generated code that can be derived from an existing macro variable.

cynthia
deleted_user
Not applicable
Thank you all for your help. Your examples not only clarified my doubt, but also gave me the courage to enter the realm of macros!!!

I sincerely appreciate your contributions.

Cathy
Cynthia_sas
SAS Super FREQ
Hi:
This is a really good tutorial on the Macro Facility. It's an great place to start for an overview.
http://www2.sas.com/proceedings/sugi28/056-28.pdf

And, there are several SAS Press books on the Macro facility that are excellent. If you just search on "Macro", you'll find them on the SAS Press site.

And, we (SAS Education) have Macro training in both classroom form (2 days) and Live Web form (4 half-day sessions):
http://support.sas.com/training/us/crs/mac1.html

cynthia
deleted_user
Not applicable
Hi Cynthia,

Thank you very much for Jane Strope's paper on macros. I have a collection of papers on macros, but I do not know how I missed this one. It is very informative.

Yes, I do have a personal copy of 'SAS Macro Programming Made Easy', and the question I posted yesterday was from that.

I want to do advanced certification in a couple of months. What is your advice? I have already got the prep guide. Will it be sufficient?

Thanks

Cathy

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

New Learning Events in April

 

Join us for two new fee-based courses: Administrative Healthcare Data and SAS via Live Web Monday-Thursday, April 24-27 from 1:00 to 4:30 PM ET each day. And Administrative Healthcare Data and SAS: Hands-On Programming Workshop via Live Web on Friday, April 28 from 9:00 AM to 5:00 PM ET.

LEARN MORE

Discussion stats
  • 7 replies
  • 1712 views
  • 0 likes
  • 3 in conversation