Consider the following text value '1/3/2016' from a dataset. This is a badly formatted date value that i cannot correct using ANYDTDTE. as I am on SAS 9.0. In this string the day and month are also the wrong way round. This is actually 03JAN2016 in date9. format
Therefore I have attempted to correct all of the above with the following macro:
%macro date_cats();
proc sql noprint;
select scan(matchdate,1,'/'), scan(matchdate,2,'/'), strip(scan(matchdate,3,'/')) into :month, :day, :year
from test;
quit;
%let padder = 0;
%if %length(&month) < 2 %then
%let month = %sysfunc(cats(&padder., &month.));
%put &month.;
%if %length(&day) < 2 %then
%let day = %sysfunc(cats(&padder., &day.));
%put &day.;
%put %sysfunc(cats(&day., &month., &year.));
%mend;
%date_cats();
The three %put statements produce the following in the log:
01
03
132016
Can anyone tell me in the final put statement why the final CATS statement is either dropping the added '0' character or reverting back to the macro variables being joined before they were padded out?
Thanks
Don't use CATS() to generate macro variables.
First it is totally unneeded since you can concatenate macro variable values by just expanding their values next to each other. Replace
%let month = %sysfunc(cats(&padder., &month.));
with
%let month = &padder.&month.;
Second when trying to evaluate the arguments to functions like CATS() that can take either numeric or character values %SYSFUNC() will attempt to evaluate your strings to see if they are numbers. In your case they are numbers so the leading zeros disappear. In other cases you can cause SAS to generate warning messages.
Third, if you really want to convert a string like 'M/D/Y' into a string like 'DMY' then assuming the string contains valid dates then just use formats to do the conversion.
%let have=1/20/2015 ;
%let want=%sysfunc(inputn(&have,mmddyy10),ddmmyyn8);
CATS is seeing numbers and automatically converting them, unhelpfully.
Generally for macro vars you can use the following
%put &day.&month.&year.;
Related
I have a libY.tableX that have for each record some SQL strings like the ones below and other fields to write the result of their execution.
select count(*) from libZ.tableK
select sum(fieldV) from libZ.tableK
select min(dsitact) from libZ.tableK
This my steps:
the user is prompted to select a lib and table and the value is passed to the vars &sel_livraria and &sel_tabela;
My 1st block is a proc sql to get all the sql string from that record.
My 2nd block is trying to concrenate all that strings to use further on to update my table with the results. The macro %isBlank is the one recommended by Chang CHung and John King in their sas papper;
My 3th block is to execute that concrenated sql string and update the table with results.
%macro exec_strings;
proc sql noprint ;
select livraria, tabela, sql_tot_linhas, sql_sum_num, sql_min_data, sql_max_data
into :livraria, :tabela, :sql_tot_linhas, :sql_sum_num, :sql_min_data, :sql_max_data
from libY.tableX
where livraria='&sel_livraria'
and tabela='&sel_tabela';
quit;
%LET mystring1 =%str(tot_linhas=(&sql_tot_linhas));
%LET separador =%str(,);
%if %isBlank(&sql_sum_num) %then %LET mystring2=&mystring1;
%else %LET mystring2= %sysfunc(catx(&separador,&mystring1,%str(sum_num=(&sql_tot_linhas))));
%if %isBlank(&sql_min_data) %then %LET mystring3=&mystring2 ;
%else %LET mystring3= %sysfunc(catx(&separador,&mystring2,%str(min_data=(&sql_min_data))));
%if %isBlank(&sql_max_data) %then %LET mystring0=&mystring3;
%else %LET mystring0= %sysfunc(catx(&separador,&mystring3,%str(max_data=(&sql_min_data))));
%PUT &mystring0;
proc sql noprint;
update libY.tableX
set &mystring0
where livraria='&sel_livraria'
and tabela='&sel_tabela';
quit;
%mend;
My problem with the code above is that iam getting this error in my final concrenated string, &mystring0.
tot_linhas=(&sql_tot_linhas),sum_num=(&sql_tot_linhas),min_data=(&sql_min_data),max_data=(&sql_min_data)
_ _ _ _
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, BTRIM, INPUT, PUT, SUBSTRING, USER.
Any help appreciated
Ok, so i follow Tom comments and ended with a proc sql solution that works!
proc sql;
select sql_tot_linhas,
(case when sql_sum_num = '' then "0" else sql_sum_num end),
(case when sql_min_data = '' then "." else sql_min_data end),
(case when sql_max_data = '' then "." else sql_max_data end)
into:sql_linhas, :sql_numeros, :sql_mindata, :sql_mxdata
from libY.tableX
where livraria="&sel_livraria"
and tabela="&sel_tabela";
quit;
proc sql;
update libY.tableX
set tot_linhas = (&sql_linhas),
sum_num =(&sql_numeros),
min_data = (&sql_mindata),
max_data = (&sql_mxdata)
where livraria="&sel_livraria"
and tabela="&sel_tabela";
quit;
Tks Tom :)
It is very hard to tell from your description what it is you are trying to do, but there are some clear coding issues in the snippets of code you did share.
First is that macro expressions are not evaluated in string literals bounded by single quotes. You must use double quotes.
where livraria="&sel_livraria"
Second is you do not want to use any of the CAT...() SAS functions in macro code. Mainly because you don't need them. If you want to concatenate values in macro code just type them next to each other. But also because they do not work well with %SYSFUNC() because they allow their arguments to be either numeric or character so %SYSFUNC() will have to guess from the strings you pass it whether it should tell the SAS function those strings are numeric or character values.
So perhaps something like:
%let mystring=tot_linhas=(&sql_tot_linhas);
%if not %isBlank(&sql_sum_num) %then
%LET mystring=&mystring,sum_num=(&sql_tot_linhas)
;
%if not %isBlank(&sql_min_data) %then
%LET mystring=&mystring,min_data=(&sql_min_data)
;
%if not %isBlank(&sql_max_data) %then
%LET mystring=&mystring,max_data=(&sql_max_data)
;
Note that I also cleaned up some obvious errors when modifying that code. Like the extra & in the value passed to the %ISBLANK() macro and the assignment of the min value to the max variable.
But it would probably be easier to generate the strings in a data step where you can test the values of the actual variables and if needed actually use the CATX() function.
so I have a code like below
%let THIS_YEAR=2020;
%macro programall;
%do i = 2016 %to &THIS_YEAR;
%let num2 =%eval(&i-2000);
%let xxx= CAT("MP",&num2);
data t_&i.;
set table1;
where GROUP in ("&xxx");
run;
%end;
for example
when i=2016
num2 = 2016-2000;
num2 = 16;
and try to concatenate with "MP", so it should create xxx=MP16.
and try to use in where statement.
but it is causing error.
how can I create Macro Variable like "MP16"correctly, so I can use it in where clause?
Thanks
You cannot use functions in macro code, they are just treated as any other text to the macro processor. But there is no need to use a function to concatenate text in macro code. Just expand the macro variable where you want to use the text it contains.
%let xxx= MP&num2 ;
Macro variables are just text (not strings, text as in the thing you type in). So to concatenate macro variables, just put them next to each other.
%let var1=Banana;
%let var2=Pepper;
%let var3=&var1. &var2.;
%put &=var3;
You don't actually have to use the third variable of course, you could just use "&var1. &var2." or whatever in your code directly.
Try
%let THIS_YEAR=2020;
%macro programall;
%local year;
%do year = 2016 %to &THIS_YEAR;
data t_&year.;
set table1;
where GROUP in ("MP%eval(&year-2000)");
run;
%end;
%mend;
options mprint;
%programall
I'm trying to create a custom transformation within SAS DI Studio to do some complicated processing which I will want to reuse often. In order to achieve this, as a first step, I am trying to replicate the functionality of a simple APPEND transformation.
To this end, I've enabled multiple inputs (max of 10) and am trying to leverage the &_INPUTn and &_INPUT_count macro variables referenced here. I would like to simply use the code
data work.APPEND_DATA / view=work.APPEND_DATA;
%let max_input_index = %sysevalf(&_INPUT_count - 1,int);
set &_INPUT0 - &&_INPUT&max_input_index;
keep col1 col2 col3;
run;
However, I receive the following error:
ERROR: Missing numeric suffix on a numbered data set list (WORK.SOME_INPUT_TABLE-WORK.ANOTHER_INPUT_TABLE)
because the macro variables are resolved to the names of the datasets they refer to, whose names do not conform to the format required for the
SET dataset1 - dataset9;
statement. How can I get around this?
Much gratitude.
You need to create a macro that loops through your list and resolves the variables. Something like
%macro list_tables(n);
%do i=1 %to &n;
&&_INPUT&i
%end;
%mend;
data work.APPEND_DATA / view=work.APPEND_DATA;
%let max_input_index = %sysevalf(&_INPUT_count - 1,int);
set %list_tables(&max_input_index);
keep col1 col2 col3;
run;
The SET statement will need a list of the actual dataset names since they might not form a sequence of numeric suffixed names.
You could use a macro %DO loop if are already running a macro. Make sure to not generate any semi-colons inside the %DO loop.
set
%do i=1 %to &_inputcount ; &&_input&i %end;
;
But you could also use a data step to concatenate the names into a single macro variable that you could then use in the SET statement.
data _null_;
call symputx('_input1',symget('_input'));
length str $500 ;
do i=1 to &_inputcount;
str=catx(' ',str,symget(cats('_input',i)));
end;
call symputx('_input',str);
run;
data .... ;
set &_input ;
...
The extra CALL SYMPUTX() at the top of the data step will handle the case when count is one and SAS only creates the _INPUT macro variable instead of creating the series of macro variables with the numeric suffix. This will set _INPUT1 to the value of _INPUT so that the DO loop will still function.
I have defined macro variable
%let data_names = fuzzy_Data_segment EMWS2.Clus_TRAIN;
Then I have written a macro to extract and print the values from the above macro variable as:
%macro calling_data;
%do i = 1 %to 2;
%let data_name&i = %qscan(&data_names,&i);
%put &&data_name&i;
%end;
%mend;
%calling_data;
My macro code is able print the first name(fuzzy_Data_segment), but, it is only printing the part of the second name(EMWS2). what should I do to print the entire second name
Your issue is that SAS considers a period to be one of the default delimiters in macro variables. In this case, it looks like you want to be using a space to delimit items in data_names, so specify that:
%let data_name&i= %qscan(&data_names,&i., %str( ));
You're also missing semicolons in your %let statement and in your call to calling_data.
I want to create a SAS macro which takes a literal date (eg. '31may2011'd) as parameter. Inside the macro I want to transform this into a SAS date value (eg. 18778).
%macro transLiteralDate2Value(literal=);
%put literal = &literal.;
%put sasdatavalue = ???; /* how to calculate this value ? */
%mend;
%transLiteralDate2Value(literal='31may2011'd);
Is the are elegant way to achieve this? Of course I could do this by parsing the literal string, but I think there must be a better way.
I use SAS 9.1.3
This will work inside or outside of a macro. Don't forget %sysfunc() has a handy optional second parameter which will let you format the output value.
%let report_date = %sysfunc(sum('01JAN2011'd),best.);
or
%let report_date = %sysfunc(putn('01JAN2011'd,best.));
Cheers
Rob
You can do it using the %sysfunc macro function.
%macro transLiteralDate2Value(literal=);
%put literal = &literal.;
%put sasdatavalue = %sysfunc(putn(&literal.,8.));
%mend;
%transLiteralDate2Value(literal='31may2011'd);
It is handy to have a pair of simple conversion macros like mine below. See also my sas-l posting.
%macro date2num(date, informat=anydtdte.);
%*-- strip quotations and postfix d from date literal if any. --*;
%*-- quotations are intentionally doubled to prevent unmatched error --*;
%let date=%sysfunc(prxchange(s/[''""]d?//i,-1,&date));
%sysfunc(inputn(&date,&informat))
%mend date2num;
%macro num2date(num, format=date10., literal=1);
%local n;
%let n = %sysfunc(putn(&num,&format));
%if &literal %then "&n"d; %else &n;
%mend num2date;