SAS Operand Error - sas

I am getting an error when trying to run the following code:
%MACRO CreateReports;
%DO I=1 %TO &LBRNUM;
%let lbr = &&VAR&I;
%let lbrTxt = %sysfunc(putn(&LBR,z9.));
%let now = %sysfunc(date(),DATE9.);
%let mnth= %sysfunc(intnx(month, "&now"d, -1, e), yymmn6.);
%let dataAsOf = %sysfunc(intnx(month, "&now"d, -1, e), mmddyy10.);
%let fname = %sysfunc(cat(&path,&lbrTxt._TTL_Backup_&mnth,.xlsx));
%put &lbrTxt &mnth &fname;
The error reads:
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
operand is required. The condition was: &LBRNUM
I would appreciate any help. Thanks

Related

Why is the %IF statement not executing?

%macro SASIsAwful();
%let LastEnt = 12;
%let i = 1;
%do i = 1 %to &LastEnt.;
proc sql noprint;
select Diff into :CheckDiff from Trial01 where RowNum = &i.;
select ACTV_DTM into :CheckDate from Trial01 where RowNum = &i.;
create table Eval001 as select * from Trial01 where RowNum = &i.;
quit;
%if &CheckDiff. = 'N' %then %do; %put &CheckDiff.; %end;
%end;
%mend SASIsAwful;
I cannot for the life of me figure out why the %if statement did not work. Proc SQL worked fine, and I manually confirmed that it saved the value N into &CheckDiff. twelve times. Yet, for some reason, the %if statement never executed, and there was no error message. What went wrong here? Thank you.
The correct macro statement should be
%IF &CHECKDIFF = N %then ... ;
If you examine an unconditional %put, you will most likely see a log lines such as
%put INFO: &=CHECKDIFF;
--- log ---
INFO: CHECKDIFF=N;
INFO: CHECKDIFF=Y;
Richard is right.
Macrovars are always strings.
CheckDiff is just the 1-char-string N, which is different from the 3-char-string "N".
You could also write
%IF "&CHECKDIFF" = "N" %then ...
but not
%IF '&CHECKDIFF' = 'N' %then ...
because Macrovars are only replaced within double quotes, not single quotes.

SAS macro mod function callout issue

I'm trying to adjust the reporting period between 1 month and 3 months (full quarter) based on time of the month. Based on the date below, I should be getting the period between 1/1 and 3/31, but I'm getting a logic error when I run the following code, any advice on how to fix it?
ERROR: Expected close parathesis after macro function invocation not found.
ERROR: Required operator not found in expression: 0 )
ERROR: SKipping to next %END statement.
%let obsdate = '31Mar2021'd;
%let current_qtr_first_day = intnx('month', &obsdate, -2, 'B');
%let current_qtr_last_day = intnx('month', &obsdate, 0, 'E');
%let current_month_first_day = intnx('month', &obsdate, 0, 'B');
%let current_month_last_day = intnx('month', &obsdate, 0, 'E');
%if %sysfunc(MOD(month(&obsdate),3)=0 ) %then %do;
%let startdt = &obsdate_current_qtr_first_day;
%let enddt = &obsdate_current_qtr_last_day;
%end;
%else %do;
%let startdt = &obsdate_current_month_first_day;
%let enddt = &obsdate_current_month_last_day;
%end;
A few more issues than I initially realized so listing all below but overall, you shouldn't be doing this in this manner. Use a data step and CALL SYMPUTX() to create the macro variable at the end instead - less buggy and infinitely easier to code.
EVERY function call in a macro requires the %SYFUNC() wrapped around it - INTNX(), MOD(), MONTH()
Missing semicolons (Line 2, 4, 7, 9)
%DO is spelled incorrectly (DOL)
I don't know what you would want here: &obsdate_current_qtr_first_day You have no macro variable shown with that name but you have two with each portion of the name? Did you want to concatenate those values?
Functions used in macros should not have the parameters within quotes ('month' versus month)
Use the options mprint; when writing/developing macros so you can see the values in the log and get more information to help you debug the issues at hand.
options mprint;
%let obsdate = '31Mar2021'd;
%let current_qtr_first_day = %sysfunc(intnx(month, &obsdate, -2, B));
%let current_qtr_last_day = %sysfunc(intnx(month, &obsdate, 0, E));
%let current_month_first_day = %sysfunc(intnx(month, &obsdate, 0, B));
%let current_month_last_day = %sysfunc(intnx(month, &obsdate, 0, E));
%if %sysfunc(MOD(%sysfunc(month(&obsdate)), 3))=0 %then %do;
%let startdt = &obsdate._&current_qtr_first_day;
%let enddt = &obsdate._&current_qtr_last_day;
%end;
%else %do;
%let startdt = &obsdate._&current_month_first_day;
%let enddt = &obsdate._&current_month_last_day;
%end;
EDIT: Here's a data step solution that can be simplified once you have it set up for what you want.
%macro get_report_dates(obsdate=);
data _null_;
*determine if month or quarterly reporting is needed;
if month(&obsdate) in (3, 6, 9, 12) then interval = 'QTR';
else interval = 'MONTH';
start_date = intnx(interval, &obsdate, 0, 'b');
end_date = intnx(interval, &obsdate, 0, 'e');
format start_date end_date date9.;
*creates global macro variables available outside this macro;
*change format here to have it displayed as desired (eg date9);
call symputx('startdt', put(start_date, yymmddd10.), 'g');
call symputx('enddt', put(end_date, yymmddd10.), 'g');
run;
%mend;
*test for a quarter;
%get_report_dates(obsdate="01Mar2021"d);
%put &startdt.;
%put &enddt.;
*test for a month;
%get_report_dates(obsdate="01Feb2021"d);
%put &startdt.;
%put &enddt.;

Required Operator not found in Expression:

In the below code, I'm using macro variables in then statement, however any variation of code seems to be failing in one or the other.
%MACRO LOOP_I;
DATA JAV_WORK2;
set WORK.JAV_WORK1;
%do i = 1 %to 24 %by 1;
%IF FF in ('CV', 'CV1', 'CV2', 'CVA', 'CVP', 'HAS') and S_TYPE in ('ETR_CARD', 'ETR_PCP', 'ETR_TRX') %THEN MONTH&i_SALES=trx&i;
%ELSE %IF FF = 'HAS' and LENGTH(GEO_ID) = 6 and S_TYPE = ('ETR_DDD') %THEN MONTH&i_SALES= UNIT&i;
%end;
RUN;
%MEND LOOP_I;
%LOOP_I
When I tried removing % from IF statements, then I was receiving "ERROR 180-322: Statement is not valid or it is used out of proper order". TIA
You can achieve the same results using arrays instead of macro loops.
data jav_work2;
set jav_work1;
array sales_month[24];
array trx[24];
array unit[24];
if(FF IN('CV', 'CV1', 'CV2', 'CVA', 'CVP', 'HAS')
AND S_TYPE in ('ETR_CARD', 'ETR_PCP', 'ETR_TRX')
then do;
do i = 1 to 24;
sales_month[i] = trx[i];
end;
else if(FF = 'HAS' AND length(GEO_ID) = 6 AND S_TYPE = 'ETR_DDD') then do;
do i = 1 to 24
sales_month[i] = unit[i];
end;
end;
end;
run;
If you wanted to use the same naming convention, you can enclose it within a macro and modify the sales_month[24] array statement with this:
array sales_month[24] %do i = 1 %to 24; month&i._sales %end; ;

Macro variable contains another substrings in either order SAS

I'm trying validate if a char macro variable contains another substrings in either order.
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test/minoperator;
%if Coop Fin in &variable %then %put i = 1;
%else %put i = 0;
%mend;
%Test;
Coop Fin in &variable resolves to TRUE, but Coop TDC resolves to FALSE.
Are there anything form to do it, without import order?
You can do a regular expression matching, the logic below ignores the order:
Solution:
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test/minoperator;
%if %sysfunc(prxmatch('Coop',"&variable.")) & %sysfunc(prxmatch('TDC',"&variable.")) %then %put i = 1;
%else %put i = 0;
%mend;
%Test;
Output:
MLOGIC(TEST): Beginning execution.
SYMBOLGEN: Macro variable VARIABLE resolves to Coop Fin TDC Real Telco
SYMBOLGEN: Macro variable VARIABLE resolves to Coop Fin TDC Real Telco
MLOGIC(TEST): %IF condition %sysfunc(prxmatch('Coop',"&variable.")) & %sysfunc(prxmatch('TDC',"&variable.")) is TRUE
MLOGIC(TEST): %PUT i = 1
i = 1
MLOGIC(TEST): Ending execution.
If you want to check if ANY of the words exist then you need to parse in string and run for each word:
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test(input) /minoperator;
%local j n str;
%let n=%sysfunc(countw(&input));
%let i=0;
%do j=1 %to &n;
%let str = %scan(&input,&j);
%if &str in &variable %then %let i = 1;
%else %put i = 0;
;
%end;
%put i = &i;
%mend;
%Test(Coop Fin);
%Test(Coop TDC);
If you want all words then you need count the number of times it resolves to true and only output if that is equal to the count.
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test(input) /minoperator;
%local j n str;
%let n=%sysfunc(countw(&input));
%let i=0;
%do j=1 %to &n;
%let str = %scan(&input,&j);
%if &str in &variable %then %let i = %eval(&i+1);
;
%end;
%if &i=&n %then
%put i = &i;
%else %put i = 0;
;
%mend;
%Test(Coop Fin);
%Test(Coop TDC x);

SAS Is it possible not to use "TO" in a do loop in MACRO?

I used to use a %do ... %to and it worked fine , but I when I tried to list all character values without %to I got a message ERROR: Expected %TO not found in %DO statement
%macro printDB2 ;
%let thisName = ;
%do &thisName = 'Test1' , 'Test2' , 'Test3' ;
proc print data=&thisName ;
run ;
%end ;
%mend printDB2 ;
I know how to complete this task using %to or %while . But I am curious is it possible to list all character values in the %do ? How can I %do this ?
If your goal here is to loop through a series of character values in some macro logic, one approach you could take is to define corresponding sequentially named macro variables and loop through those, e.g.
%let mvar1 = A;
%let mvar2 = B;
%let mvar3 = C;
%macro example;
%do i = 1 %to 3;
%put mvar&i = &&mvar&i;
%end;
%mend example;
%example;
Alternatively, you could scan a list of values repeatedly and redefine the same macro var multiple times within your loop:
%let list_of_values = A B C;
%macro example2;
%do i = 1 %to 3;
%let mvar = %scan(&list_of_values, &i, %str( ));
%put mvar = &mvar;
%end;
%mend example2;
%example2;
I've explicitly specified that I want to use space as the only list delimiter for scan - otherwise SAS uses lots default delimiters.