How can I transform this code into macro? - sas

So I'd like to do a macro code mixed with proc sql and data step. I have the following code in SAS:
data work.calendar;
set work.calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
run;
I'd like to put it into macro code, but it doesn't work.
My macro code:
%macro1();
data work.job_calendar;
set work.job_calendar;
%if business_day_count^=-1 %then %do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
%end;
else
business_day_count = -1;
run;
%mend;
The whole code is:
%macro update_day(date);
proc sql;
update work.job_calendar
set business_day_count =
case when datepart(calendar_date) = "&date"d then -1
else business_day_count
end;
quit;
proc sql;
update work.job_calendar
set status_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set daily_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set weekly_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
proc sql;
update work.job_calendar
set monthly_rundate_ind =
case when business_day_count = -1 then 'N'
else status_ind
end;
quit;
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
%mend;
The error code is: ERROR 180 - 322 Statement is not valid or it is used out of proper order. I don't know what I'm doing wrong.

You're mixing data step and macro code. Not sure what you're trying to achieve so no idea on what to propose but removing the %IF/%THEN will allow your code to work.
%macro1();
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else
business_day_count = -1;
run;
%mend;
%macro1;
Here is a tutorial on converting working code to a macro and one on overall macro programming.

To define a macro you need to use the %MACRO statement. Also why did you change lines 3 and 7 from data statements to macro statements?
That code cannot work. First the %IF is always true since the string business_day_count is never going to match the string -1. Second you have an else statement without any previous if statement.
Try something like this instead.
%macro macro1();
data work.job_calendar;
set work.job_calendar;
if business_day_count^=-1 then do;
num_seq + 1;
drop num_seq;
business_day_count = num_seq;
end;
else business_day_count = -1;
run;
%mend macro1;

Related

Count and generate a constant in SAS

For each id count the number of qc=fails, generate a new column final with all values as "repeat" if any one id has 2 or more than 2 fails, else pass. Repeat if count is greater than equal to 2. For some reason the if-else statement does not work. Instead of the final being a repeat for count greater than equal to 2, it is displaying it as pass.dataset
%let repeat_flag = 0;
%let pass_flag = 0;
data _null_;
set exp;
if count ge 2 then call symputx ('repeat_flag',1);
else call symputx ('pass_flag',1);
stop;
run;
%if &repeat_flag %then %do;
data exp;
set exp;
Final = 'REPEAT';
run;
%end;
%if &pass_flag %then %do;
data exp;
set exp;
Final = 'PASS';
run;
%end;
%put &repeat_flag;
%put &pass_flag;
If you want to test if any observation has COUNT larger than one then use a pattern like this instead. Set the macro variable to the FALSE condition and then set it TRUE when the condition is met.
%let pass_flag=PASS;
data _null_;
set exp;
if count ge 2 then do;
call symputx ('pass_flag','REPEAT');
stop;
end;
run;
data want;
set exp;
length final $6;
final="&pass_flag" ;
run;

If statement in SAS macro not always working

I'm relatively new to SAS coding, and I'm trying to write this (see the whole code down below) macro that should evaluate performances (to_worse, to_better, recoveded, etc.) of several teams.
The problem is that this rule here:
if team_&y.="&PROVIDER." and team_&i. eq "" and balance_&i. = 0 then do; regolarizzato = "regolarizzato"; end;
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then do; to_worse="to_worse"; end;
else if team_&i. = team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then do; to_better = "to_better"; end;
does not seem to be always properly wowking; it works most of the times, but not always, and I don't understand the reason why it's failing sometimes. Could any kindheardted fella please explain why this happens? Thank you so much in advance!
%let oggi = '07oct2022'd;
%let mese = %sysfunc (MONTH(&oggi.));
%IF &mese. < 10 %THEN %do; %let mese = 0&mese.; %end;
%let giorno = %sysfunc (DAY(&oggi.));
%let anno = %sysfunc(Year(&oggi.));
/*%IF &giorno. < 10 %THEN %do; %let giorno = 0&giorno.; %end;*/
%macro COSTI_1;
%let i = 0;
%DO i = 0 %TO &giorno.;
data COSTI_&i.;
set data.initial_db_&mese.;
format balance_&i. commax14.2;
keep contract_number team_&i. balance_&i.;
run;
%end;
%mend;
%COSTI_1;
data COSTI_db;
set COSTI_0;
run;
%macro COSTI_2;
%let i = 1;
%DO i = 1 %TO &giorno.;
PROC SQL;
CREATE TABLE COSTI_db AS
SELECT
A.*,
B.*
FROM COSTI_db AS A LEFT JOIN COSTI_&i. AS B
ON (A.contract_number = B.contract_number);
QUIT;
run;
%end;
%mend;
%COSTI_2;
data COSTI_db;
set COSTI_db;
length team $ 20;
format team $CHAR30.;
team="altro";
run;
%MACRO COSTI_PROVIDER (PROVIDER);
data COSTI_db_&Provider.;
set COSTI_db;
run;
%macro COSTI_A;
%let i = 0;
%DO i = 0 %TO &giorno.;
data COSTI_db_&Provider.;
set COSTI_db_&Provider.;
if team_&i. = "&PROVIDER." then team = "&PROVIDER.";
run;
%end;
%mend;
%COSTI_A;
DATA COSTI_&PROVIDER.;
set COSTI_db_&Provider. (where =(team="&PROVIDER."));
length to_worse $ 20;
format to_worse $CHAR30.;
length to_better $ 20;
format to_better $CHAR30.;
length regolarizzato $ 20;
format regolarizzato $CHAR30.;
to_worse="no";
to_better="no";
regolarizzato="no";
run;
%macro to_worse;
%let i = 1;
%let y = %eval(&i.-1);
%DO i = 1 %TO &giorno.;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
if team_&y.="&PROVIDER." and team_&i. eq "" and balance_&i. = 0 then do; regolarizzato = "regolarizzato"; end;
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then do; to_worse="to_worse"; end;
else if team_&i. = team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then do; to_better = "to_better"; end;
run;
%end;
%mend;
%to_worse;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
length esito_finale $ 20;
format esito_finale $CHAR30.;
format balance_affido commax12.2;
if to_worse="to_worse" then esito_finale="to_worse";
else if regolarizzato = "regolarizzato" then esito_finale="regolarizzato";
else if to_better = "to_better" then esito_finale = "to_better";
else if team_&giorno. = "&PROVIDER." then esito_finale = "in_gestione_oggi";
if richiamo_o_repo = "&PROVIDER." and inflows < -1 then esito_finale = "richiamo";
if richiamo_o_repo = "&PROVIDER." and to_normal > 1 then esito_finale = "repo";
if team_0 = "&PROVIDER." then balance_affido = balance_0;
else balance_affido = -1;
drop INFLOWS TO_NORMAL RICHIAMO_O_REPO;
run;
%macro COSTI_B;
%let i = 0;
%DO i = 1 %TO &giorno.;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
if team_&i. = "&PROVIDER." and balance_affido = -1 then balance_affido=balance_&i.;
run;
%end;
%mend;
%COSTI_B;
proc sql;
create table RIEPILOGO_&PROVIDER.
as select esito_finale, sum(balance_affido) as somma_balance_affido
from COSTI_&PROVIDER.
group by esito_finale;
quit;
data RIEPILOGO_&PROVIDER.;
set RIEPILOGO_&PROVIDER.;
format somma_balance_affido commax12.2;
run;
%MEND;
%COSTI_PROVIDER(TEAM_A);
%COSTI_PROVIDER(TEAM_B);
%COSTI_PROVIDER(TEAM_C);
%COSTI_PROVIDER(TEAM_D);
The macro that is generating that IF statement seems to have few issues.
First the macro variable Y is always going to be 0 since it is set to %eval(1-1). Did you intend Y to always be one less than I? If so move that %LET statement inside the %DO loop.
Second you keep reading and writing the same dataset over and over. You should probably move the %DO loop so that it can all be done with one data step.
%macro to_worse;
%local i y ;
data COSTI_&PROVIDER.;
set COSTI_&PROVIDER.;
%do i = 1 %TO &giorno.;
%let y = %eval(&i.-1);
if team_&i. eq "" and team_&y. eq "&PROVIDER." and balance_&i. = 0 then regolarizzato = "regolarizzato";
else if team_&i. ne team_&y. and team_&y. eq "&PROVIDER." and team_&i. ne "" then to_worse="to_worse";
else if team_&i. eq team_&y. and team_&y. eq "&PROVIDER." and balance_&y. > balance_&i. then to_better = "to_better";
%end;
run;
%mend to_worse;
And finally your set of IF/THEN/ELSE conditions do not cover all of the possible combinations of values of TEAM_&I, TEAM_&Y, BALANCE_&I and BALANCE_&Y. Are you sure that the situations where you think it is not doing what you want are not just the situations that fall into one of those uncovered combinations?

Assigning let variables to WHERE statement using DATA NULL

hi I am trying to use DATA NULL step to assign value to variable based on different criteria. This variable from NULL statement will be assigned to WHERE statement in the following DATA step.
Ideally if I run it today(Thursday which is 5) the code should return 30APR2019 for both the variables. But my code throws only the variable value in the LAST-IF- statement.
data _null_;
if weekday(today()) = 5 then do;
%let exc_st_day = '30APR2019'd;
%let exc_en_day = '30APR2019'd;
end;
else if weekday(today()) = 6 then do;
%let exc_st_day = '01MAY2019'd;
%let exc_en_day = '01MAY2019'd;
end;
else if weekday(today()) = 2 then do;
%let exc_st_day = '02MAY2019'd;
%let exc_en_day = '02MAY2019'd;
end;
else if weekday(today()) = 3 then do;
%let exc_st_day = '03MAY2019'd;
%let exc_en_day = '03MAY2019'd;
end;
else if weekday(today()) = 4 then do;
%let exc_st_day = '04MAY2019'd;
%let exc_en_day = '06MAY2019'd;
end;
%put &exc_st_day &exc_en_day;
run;
You need to use CALL SYMPUTX() to create macro variables, not %LET within a data step.
if weekday(today()) = 5 then do;
call symputx('exc_st_day', '30APR2019'd);
call symputx('exc_en_day', '30APR2019'd);
end;
Macro code is evaluated BEFORE the SAS code that it generates runs. So you told SAS to run this code:
%let exc_st_day = '30APR2019'd;
%let exc_en_day = '30APR2019'd;
%let exc_st_day = '01MAY2019'd;
%let exc_en_day = '01MAY2019'd;
%let exc_st_day = '02MAY2019'd;
%let exc_en_day = '02MAY2019'd;
%let exc_st_day = '03MAY2019'd;
%let exc_en_day = '03MAY2019'd;
%let exc_st_day = '04MAY2019'd;
%let exc_en_day = '06MAY2019'd;
%put &exc_st_day &exc_en_day;
data _null_;
if weekday(today()) = 5 then do;
end;
else if weekday(today()) = 6 then do;
end;
else if weekday(today()) = 2 then do;
end;
else if weekday(today()) = 3 then do;
end;
else if weekday(today()) = 4 then do;
end;
run;
If you want to create macro variable values from a data step use the CALL SYMPUTX() function. Or if your really need to insert leading and/or trailing spaces into the macro variable value use the older CALL SYMPUT() function.

How to call global macro variable

I have created two global macro variables.
date=10/6/2016
b=simple
I want to create a dataset with variable which refers above macros
below are the variables which i want based on condition.
varible1: Date- which the refers the date macro variable
variable2:condition- if b =simple then condition =y or if b is not equal to simple then condition =n
So dateset will look like below
Date condition
10/6/2016 y
You have two options.
Number one (using macro-variables in a data step):
data result;
format date ddmmyy10.;
date = input("&date", ddmmyy10.);
if "&b" = "simple" then condition = "y";
else condition = "n";
run;
Number two (create macro and generate SAS code based on a macro-variable value):
%macro test;
data result;
format date ddmmyy10.;
date = input("&date", ddmmyy10.);
%if &b = simple %then %do;
condition = "y";
%end;
%else %do;
condition = "n";
%end;
run;
%mend test;
%test
Edited:
Or using PROC SQL:
%let date=12/10/2016;
%let b=simple;
proc sql;
create table result
(
date num format=ddmmyy10.,
condition char(1)
);
quit;
%macro insert;
proc sql;
%if &b = simple %then %do;
%let condition = y;
%end;
%else %do;
%let condition = n;
%end;
insert into result (date, condition) values (%sysfunc(inputn(&date, ddmmyy10.)), "&condition");
quit;
%mend insert;
%insert

How to loop through a macro variable in SAS

I have an example like this:
proc sql;
select dealno into :deal_no
from deal_table;
Now I want to traverse the variable deal_no now containing all dealno in table deal_table but I don't know how to do it.
Another option is add 'separated by' to the sql code, which will add a delimiter to the values. You can then use the SCAN function in a data step or %SCAN in a macro to loop through the values and perform whatever task you want. Example below.
proc sql noprint;
select age into :age separated by ','
from sashelp.class;
quit;
%put &age.;
data test;
do i=1 by 1 while(scan("&age.",i) ne '');
age=scan("&age.",i);
output;
end;
drop i;
run;
If you do
%put &deal_no;
you can see that it only contains the first value of dealno, not all of them.
To avoid that you can do something like this:
proc sql;
create table counter as select dealno from deal_table;
select dealno into :deal_no_1 - :deal_no_&sqlobs
from deal_table;
quit;
%let N = &sqlobs;
%macro loop;
%do i = 1 %to &N;
%put &&deal_no_&i;
%end;
%mend;
%loop; run;
Here's another solution.
proc sql noprint;
select age into :ageVals separated by ' '
from ageData;
quit;
%put &ageVals;
%macro loopAgeVals; %let i = 1; %let ageVal = %scan(&ageVals, &i);
%do %while("&ageVal" ~= "");
%put &ageVal;
%let i = %eval(&i + 1);
%let ageVal = %scan(&ageVals, &i);
%end;
%mend;
%loopAgeVals;