Remove single quotes in list of values in macro variable - sas

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.

Related

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

SAS format char

First i have created this table
data rmlib.tableXML;
input XMLCol1 $ 1-10 XMLCol2 $ 11-20 XMLCol3 $ 21-30 XMLCol4 $ 31-40 XMLCol5 $ 41-50 XMLCol6 $ 51-60;
datalines;
| AAAAA A||AABAAAAA|| BAAAAA|| AAAAAA||AAAAAAA ||AAAA |
;
run;
I want to clean, concatenate and export. I have written the following code
data rmlib.tableXML_LARGO;
file CleanXML lrecl=90000;
set rmlib.tableXML;
array XMLCol{6} ;
array bits{6};
array sqlvars{6};
do i = 1 to 6;
*bits{i}=%largo(XMLCol{i})-2;
%let bits =input(%largo(XMLCol{i})-2,comma16.5);
sqlvars{i} = substr(XMLCol{i},2,&bits.);
put sqlvars{i} &char10.. #;
end;
run;
the macro largo count how many characters i have
%macro largo(num);
length(put(&num.,32500.))
%mend;
What i need is instead of have char10, i would like that this number(10) would be the length, of each string, so to have something like
put sqlvars{i} &char&bits.. #;
I don't know if it possible but i can't do it.
I would like to see something like
AAAAA AAABAAAAA BAAAAA AAAAAAAAAAAAA AAAA
It is important to me to keep the spaces(this is only an example of an extract of a xml extract). In addition I will change (for example) "B" for "XPM", so the size will change after cleaning the text, that it what i need to be flexible in the char
Thank you for your time
Julen
I'm still not quite sure what you want to achieve, but if you want to combine the text from multiple varriables into one variable, then you could do something along the lines:
proc sql;
select name into :names separated by '||'
from dictionary.columns
where 1=1
and upcase(libname)='YOURLIBNAME'
and upcase(memname)='YOURTABLENAME';
quit;
data work.testing;
length resultvar $ 32000;
set YOURLIBNAME.YOURTABLENAME;
resultvar = &names;
resultvar2 = compress(resultvar,'|');
run;
Wasn't able to test this, but this should work if you replace YOURLIBNAME and YOURTABLENAME with your respective tables. I'm not 100% sure if the compress will preserve the spaces in the text.. But I think it should.
The format $VARYING. <length-variable> is a good candidate for solving this output problem.
On the presumption of having a number of variables whose values are vertical-bar bounded and wanting to output to a file the concatenation of the values without the bounding bars.
data have;
file "c:\temp\want.txt" lrecl=9000;
length xmlcol1-xmlcol6 $100;
array values xmlcol1-xmlcol6 ;
xmlcol1 = '| A |';
xmlcol2 = '|A BB|';
xmlcol3 = '|A BB|';
xmlcol4 = '|A BBXC|';
xmlcol5 = '|DD |';
xmlcol6 = '| ZZZ |';
do index = 1 to dim(values);
value = substr(values[index], 2); * ignore presumed opening vertical bar;
value_length = length(value)-1; * length with still presumed closing vertical bar excluded;
put value $varying. value_length #; * send to file the value excluding the presumed closing vertical bar;
end;
run;
You have some coding errors in that is making it difficult to understand what you want to do.
Your %largo() macro doesn't make any sense. There is no format 32500.. The only reason it would run in your code is because you are trying to apply the format to a character variable instead of a number. So SAS will automatically convert to use the $32500. instead.
The %LET statement that you have hidden in the middle of your data step will execute BEFORE the data step runs. So it would be less confusing to move it before the data step.
So replacing the call to %largo() your macro variable BITS will contain this text.
%let bits =input(length(put(XMLCol{i},32500.))-2,comma16.5);
Which you then use inside a line of code. So that line will end up being this SAS code.
sqlvars{i} = substr(XMLCol{i},2,input(length(put(XMLCol{i},$32500.))-2,comma16.5));
Which seems to me to be a really roundabout way to do this:
sqlvars{i} = substr(XMLCol{i},2,length(XMLCol{i})-2);
Since SAS stores character variables as fixed length, it will pad the value stored. So what you need to do is to remember the length so that you can use it later when you write out the value. So perhaps you should just create another array of numeric variables where you can store the lengths.
sqllen{i} = length(XMLCol{i})-2;
sqlvars{i} = substr(XMLCol{i},2,sqllen{i});

SAS &SYSERRORTEXT variable removing quote to use in SQL

I'm trying to use SAS system variables to track batch program execution (SYSERRORTEXT, SYSERR, SYSCC, ...)
I want to insert those in a dataset, using PROC SQL, like this :
Table def :
PROC SQL noprint;
CREATE TABLE WORK.ERROR_&todaydt. (
TRT_NM VARCHAR(50)
,DEB_EXE INTEGER FORMAT=DATETIME.
,FIN_EXE INTEGER FORMAT=DATETIME.
,COD_ERR VARCHAR(10)
,LIB_ERR VARCHAR(255)
,MSG_ERR VARCHAR(1000)
)
;
RUN;
QUIT;
Begin execution :
PROC SQL noprint;
INSERT INTO WORK.ERROR_&todaydt. VALUES("&PGM_NAME", %sysfunc(datetime), ., '', '', '';
RUN;
QUIT;
End execution :
PROC SQL noprint;
UPDATE WORK.ERROR_&todaydt.
SET
FIN_EXE = %sysfunc(datetime())
,COD_ERR = "&syserr"
,LIB_ERR = ''
,MSG_ERR = "&syserrortext"
WHERE TRT_NM = "&PGM_NAME"
;
RUN;
QUIT;
The problem occurs with system variable &syserrortext. which may contains special char, espcially single quote ('), like this
Code example for problem :
DATA NULL;
set doesnotexist;
RUN;
and so, &syserrortext give us : ERROR: Le fichier WORK.DOESNOTEXIST.DATA n'existe pas.
my update command is failing with this test, so how i can remove special chars from my variable &syserrortext ?
One approach, which avoids the need to remove special characters, is to simply use symget(), eg as follows:
,MSG_ERR = symget('syserrortext')
Make sure to quote the value. First use macro quoting in case the value contains characters that might cause trouble. Then add quotes so that the value becomes a string literal. Use the quote() function to add the quotes in case the value contains quote characters already. Use the optional second parameter so that it uses single quotes in case the value contains & or % characters.
,MSG_ERR = %sysfunc(quote(%superq(syserrortext),%str(%')))