SAS % let statement - sas

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.

Related

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.

SAS call symput in data step

I am facing this issue with sas data step. My requirement is to get a list of variables such as
total_jun2018 = sum(jun2018, dep_jun2018);
total_jul2018 = sum(jul2018, dep_jul2018);
Data final4;
set final3;
by hh_no;
do i=0 to &tot_bal_mnth.;
bal_mnth = put(intnx('month',"&min_Completed_dt."d, i-1), monyy7.);
call symputx('bal_mnth', bal_mnth);
&bal_mnth._total=sum(&bal_mnth., Dep_&bal_mnth.);
output;
end;
But I am facing error that macro variable bal_mnth not resolved. Also once it did ran successfully but I want that output must be printed sequentially but it only prints output for last loop when i=6 then it prints only Total_DEC2018=sum(DEC2018, DEP_DEC2018);
Any help will be appreciated!
Thanks,
Ajay
This is a common issue when learning SAS Macro. The problem is that the macro processor needs to resolve &bal_mnth to a value when the data step is first submitted for execution, but the CALL SYMPUT doesn't execute until the data step is actually executed, so at the time you submit the code, there is no value available for &bal_mnth.
In this case you don't need bal_mnth to be created as a variable in the data set, so you could replace the line that starts bal_mnth = put(intck(...)) with a %let bal_mnth = ... statement. The %let executes while the data step is being submitted, so that way its value will be available when you need it.
My proposed %let statement will need to wrap the functions in at least one SYSFUNC call, which is left as an exercise for the reader :-)
It looks like you want to generate a series of assignment statements like:
total_jun2018 = sum(jun2018, dep_jun2018);
total_jul2018 = sum(jul2018, dep_jul2018);
...
total_jan2019 = sum(jan2019, dep_jan2019);
What is known as wallpaper code.
If your variables names were easier, such as dep1 to dep18 then it would be easy to use arrays to process the data. With your current naming convention the problem with generating the array statements is not much different than the problem of generating a series of assignment statements.
You can create a macro so that you could use a %DO loop to generate your wallpaper code.
%local i bal_mnth;
%do i=0 %to &tot_bal_mnth.;
%let bal_mnth = %sysfunc(intnx(month,"&min_Completed_dt."d, &i-1), monyy7.);
total_&bal_mnth = sum(&bal_mnth , Dep_&bal_mnth );
%end;
Or you could just generate the code to a file with a data step.
%let tot_bal_mnth = 7;
%let min_Completed_dt=01JUN2018;
filename code temp;
data _null_;
file code;
length bal_mnth $7 ;
do i=0 to &tot_bal_mnth.;
bal_mnth = put(intnx('month',"&min_Completed_dt."d, i-1), monyy7.);
put 'total_' bal_mnth $7. ' = sum(' bal_mnth $7. ', Dep_' bal_mnth $7. ');';
end;
run;
So the generated file of code looks like this:
total_MAY2018 = sum(MAY2018, Dep_MAY2018);
total_JUN2018 = sum(JUN2018, Dep_JUN2018);
total_JUL2018 = sum(JUL2018, Dep_JUL2018);
total_AUG2018 = sum(AUG2018, Dep_AUG2018);
total_SEP2018 = sum(SEP2018, Dep_SEP2018);
total_OCT2018 = sum(OCT2018, Dep_OCT2018);
total_NOV2018 = sum(NOV2018, Dep_NOV2018);
total_DEC2018 = sum(DEC2018, Dep_DEC2018);
You can then use %include to run it in your data step.
data final4;
set final3;
by hh_no;
%include code / source2 ;
run;
I would like to offer another point of view: the difficulty you are having here results from the use of a wide data shape, with lots of columns.
Rather than working with your data in this shape, you could first transpose from wide to long, so that instead of having lots of total_xxx columns you just have 3: total, total_dep and date, with one row per month. Once it's in this format, it will be much easier to work with, potentially allowing you to avoid resorting to macros and wallpaper code.
Suggested reading:
Transpose wide to long with dynamic variables

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.

JCL: How to read file name and find the specific string

I need to read the file name in JCL and find specific string in that. If string present then i need to set the flag variable.
Example:
000063 //SETVARS SET RUNMODE=AY,
000064 // MGRTMODE=M,
000065 // PARMFILE=BASXXXX.T1.XXXX.JIRA.T011746
If "PARMFILE" contains "JIRA", then set JIRAFLAG = Y else JIRAFLAG = N. I need to pass the JIRAFLAG to another JCL/SAS job to process further.
Thanks!
Bharathi
SAS:
000083 /**/
000084 %LET CHKFILE = %SCAN(&SYSPARM,1,+);
000085 %put &CHKFILE ;
000086 %GLOBAL JIRAFLG ;
000087 %MACRO CHK ;
000088 %LET TSTVAL = %SCAN(&CHKFILE,4,.) ;
000089 %PUT &TSTVAL;
000090 %IF &TESTVAL EQ 'JIRA' %THEN %LET JIRAFLG = 'Y' ;
000091 %ELSE %LET JIRAFLG = 'N' ;
000092 %PUT &JIRAFLG ;
000093 %MEND CHK;
000094 %CHK ;
JCL does not provide a scripting language per se although it is intuitive to think of it that way because of the JCL Symbols and conditionals.
I'm not a SAS buy but it seems like your SAS sample does the scanning. I'd suggest that you have a STEP where you parse the PARMFILE statement and set a known return code of your choice. For instance, choose a return code of 1.
Follow that with another step that when the return code is 1 executes the program you want to process when JIRA is in the PARMFILE. Since you mentioned a seperate job you could submit another job through INTRDR.
There are ways to accomplish what you want you just need to be creative. As I said, the conditionals and JCL Symbols lure many people into thinking scripting which is unfortunately not how it works.
SRCHFOR 'YOURSTRING',W (https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.f54u200/sfcmdfgrr.htm)
and then check here for conditional processing
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.ieab600/iea3b6_Examples_of_IF_THEN_ELSE_ENDIF_statement_constructs.htm