Macro variable from other macro variable - sas

Let say I want to create a macro variable like:
%let year=2019;
%let ye = SUBSTR(%year,3,2) (I want to extract 19 from 2019).
But it does not work. Do we have any of creating such a macro variable like this?
Update: My code in fact is like
%let yearend = 2019
% let monthend = 04
And now I want to create a macro variable like "data1904" in order to use the dataset named "data1904" in my library. So what I want to create is something like
%let dataname= CAT(SUBSTR(%yearend,3,2),%monthend)

You use & to reference a macro variable, not %.
When you use a function in macro code you need to wrap it in %SYSFUNC() so the compiler can differentiate between text and code.
%let ye = %sysfunc(substr(&year, 3, 2));
Or alternatively there is the %SUBSTR() macro function which allows you to skip the %SYSFUNC().
%let ye = %substr(&year, 3, 2);

Related

SAS % let statement

I am facing an issue with the % let statement I have not found yet the correct answer, and I am not sure if something can be done.
I am facing the following issue, hope someone can help.
I have this macro statement.
%let num = 5;
%let c&num.act = %str(re&num);
I want that the %let c&num.act gets the value c5.act, but it is not working this way.
So the last let statement is resolve this way:
c5.act = re5
Can you give me some help?
It sounds like the question is NOT about %LET statements. Instead you seem to be confused about how to reference the macro variable that you created with the %LET statement.
So these two statements
%let num=5;
%let c&num.act = re#
create two macro variables. NUM and C5ACT.
To see the values you can use code like this:
%put #
%put &&c&num.act ;
When you have two & triggers next to each other the macro processor will replace them with a single & and remind itself to re-process the resulting string for further macro expansion.
On the first pass &&c&num.act will be transformed into &c5act which will then be re-evaluated and result in value re5 that was assigned by the second %LET statement.

SAS - referring to a folder based on date

In my SAS code I would like to refer to an existing folder based on the date value (LADE_DATUM) that I declare with a prompt. From this date I define LADE_JAHR and LADE_MONAT:
%let LADE_JAHR = %sysfunc(year("&LADE_DATUM"D));
%let LADE_MONAT = %sysfunc(month("&LADE_DATUM"D));
Based on these two variables I would like to refer to some existing folders (importpfad) that look like:
2020-09, 2020-10, 2020-11, 2020-12, etc.
This is the code:
data _null_;
if &lade_monat < 10 then a = '0'; else a = '';
call symput('a',a);
%let importpfad = /folderx/Input_Files/**&lade_jahr/&lade_jahr.-&a.&lade_monat**/;
The problem is, if a = '' then the folder it refers to looks like "2020- 10" instead of "2020-10".
So there is a space between that I don't want to have.
If a is between 1 and 9, everything is OK.
Just tell %SYSFUNC() you want the month number generated with the Z format instead of the default best format.
%let LADE_MONAT = %sysfunc(month("&LADE_DATUM"D),Z2.);
%let importpfad = /folderx/Input_Files/&lade_jahr/&lade_jahr.-&lade_monat/;

Calling macros from within a datastep

Maintaining somebody else's SAS project, I found some code snippet which creates a table input-stats within a data step. The variable &all. contains a list of tables to examine. The data step is rather long which I shortened here with /* more code */:
%let all = "work.table1*mywork.table2";
data input-stats;
i = 1;
do while (scan(&all., i, '*') ne '');
name = scan(&all., i, '*');
/* more code */
output;
i = i + 1;
end;
run;
I want to expand the table input-stat with yet another column giving me the number of lines of each table. I found the following macro within the project to do exactly this:
%macro count_rows(ds);
%let DSID=%sysfunc(OPEN(&DS.,IN));
%let NOBS=%sysfunc(ATTRN(&DSID.,NOBS));
%let RC=%sysfunc(CLOSE(&DSID.));
&nobs
%mend;
I would like to now integrate this call within the above mentioned data step, but obviously, I cannot just simply add a rows=%count_rows(name) (e.g. instead of /* more code */) as I would in other programming languages.
How would you solve this issue with minimal code modifications? Is there a way without making a huge %while loop?
The functionality of the macro code can be replicated in DATA Step scope by invoking the same functions. No need for macro and intermingling scopes that can be confusing when using RESOLVE or CALL EXECUTE.
...
name = scan(&all., i, '*');
/* more code */
* add row counting code here;
_dsid = open (name,'IN');
nobs = attrn(_dsid,'NOBS');
_dsid = close (_dsid);
drop _:;
output;
...

Remove single quotes in list of values in macro variable

I have a project with multiple programs. Each program has a proc SQL statement which will use the same list of values for a condition in the WHERE clause; however, the column type of one database table needed is a character type while the column type of the other is numeric.
So I have a list of "Client ID" values I'd like to put into a macro variable as these IDs can change, and I would like to change them once in the variable instead of in multiple programs.
For example, I have this macro variable set up like so and it works in the proc SQL which queries the character column:
%let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
Proc SQL part:
...IN &CLNT_ID_STR.
I would like to create another macro variable, say CLNT_ID_NUM, which takes the first variable (CLNT_ID_STR) but removes the quotes.
Desired output: (179966, 200829, 201104, 211828, 264138)
Proc SQL part: ...IN &CLNT_ID_NUM.
I've tried using the sysfunc, dequote and translate functions but have not figured it out.
TRANSLATE doesn't seem to want to allow a null string as the replacement.
Below uses TRANSTRN, which has no problem translating single quote into null:
1 %let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
2 %let want=%sysfunc(transtrn(&clnt_id_str,%str(%'),%str())) ;
3 %put &want ;
(179966, 200829, 201104, 211828, 264138)
It uses the macro quoting function %str() to mask the meaning of a single quote.
Three other ways to remove single quotes are COMPRESS, TRANSLATE and PRXCHANGE
%let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
%let id_list_1 = %sysfunc(compress (&CLNT_ID_STR, %str(%')));
%let id_list_2 = %sysfunc(translate(&CLNT_ID_STR, %str( ), %str(%')));
%let id_list_3 = %sysfunc(prxchange(%str(s/%'//), -1, &CLNT_ID_STR));
%put &=id_list_1;
%put &=id_list_2;
%put &=id_list_3;
----- LOG -----
ID_LIST_1=(179966, 200829, 201104, 211828, 264138)
ID_LIST_2=( 179966 , 200829 , 201104 , 211828 , 264138 )
ID_LIST_3=(179966, 200829, 201104, 211828, 264138)
It really doesn't matter that TRANSLATE replaces the ' with a single blank () because the context for interpretation is numeric.

Declaring macro variable with evaluation of function as value

I'm new to SAS. I encurred into a problem when trying to declare a macro variable with the result of some operation as value.
data _null_;
%let var1 = 12345;
%let var2 = substr(&var1., 4,5);
run;
I get that var2 has value substr(&var1., 4,5) (a string) instead of 45 as I would like. How to make the variable declaration evaluate the function?
Sorry it the question is trivial. I looked in the documentation for a bit but couldn't find an answer.
There is a macro equivalent called %substr() which can be used as follows:
%let var1 = 12345;
%let var2 = %substr(&var1., 4,2);
%put var2 = &var2;
Note that the data and run statements are not required for macro language processing and the 3rd argument to %substr() (and substr()) specifies the length you want, not the position of the last character, which is why I used 2 instead of 5.
Edit: Also, if there is no macro equivalent then you could use %sysfunc() to make use of the data step function in macro code. See the documention for full details as there are some quirks, such as not using quotes and a few exceptions to the list of data step functions that can be used.