BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
hellohere
Pyrite | Level 9

I have one macro, can be invoked.  The number 10295 is the outcome which is right. 

Somehow when I run inside the macro test() below, the macro appears not invoked and complains.

 

Anyone can help?!

 

%get_min_st_ind(zz500, 0827, 6, 0.1);

 

25006  %macro get_min_st_ind(indexout, dt, i_mm_ind, th_pct);
25007      %local _st_ind_lp_off_mm;
25008      %let _st_ind_lp_off_mm=0;
25009      proc sql noprint;
25010      select mm_ind into: _t_mm_ind from  &indexout._&dt._mm_ind where i=&i_mm_ind.;
25011      select lp_st_ind into: _t_st_ind from   &indexout._&dt._mm_ind where i=&i_mm_ind.;
25012      select lp_end_ind into: _t_end_ind from     &indexout._&dt._mm_ind where i=&i_mm_ind.;
25013      quit;
25014
25015      data _index_temp;
25016      set  &indexout._&dt._pout_wt(where=(ind between &_t_end_ind. and &_t_mm_ind.));
25017      run;
25018
25019      proc sort data=_index_temp ; by DESCENDING ind; run;quit;
25020
25021      data _index_temp; set _index_temp;
25022      retain index_max index_off_max_max;
25023      if _N_=1 then do;
25024          index_max=&indexout.; index_off_max_max=0;
25025      end;
25026      else do;
25027          if &indexout.>index_max then index_max=&indexout.;
25028          index_off_max=index_max-&indexout.;
25029          if index_off_max>index_off_max_max then index_off_max_max=index_off_max;
25030      end;
25031      run;quit;
25032
25033          /*proc sgplot data=_index_temp;
25034              series x=ind y=index_off_max_max /  lineattrs=( color= black thickness=2
25034! pattern=solid);
25035              series x=ind y=index_off_max /  lineattrs=( color= blue thickness=2 pattern=solid);
25036              series x=ind y=index_max /  lineattrs=( color=red thickness=2 pattern=solid);
25037              series x=ind y=&indexout. / y2axis lineattrs=( color=lime thickness=2
25037! pattern=solid);
25038              refline  0.1/axis=y lineattrs=(color=lime thickness=2 pattern=solid);
25039          run;quit;
25040      proc sort data=_index_temp ; by ind; run;quit;*/
25041
25042      proc sql noprint;
25043      select ind into: _st_ind_lp_off_mm from _index_temp where index_off_max_max<&th_pct. having
25043!  &indexout.=max(&indexout.);
25044      quit;
25045
25046      &_st_ind_lp_off_mm
25047  %mend;
25048
25049  %get_min_st_ind(zz500, 0827, 6, 0.1);
MLOGIC(GET_MIN_ST_IND):  Beginning execution.
MLOGIC(GET_MIN_ST_IND):  Parameter INDEXOUT has value zz500
MLOGIC(GET_MIN_ST_IND):  Parameter DT has value 0827
MLOGIC(GET_MIN_ST_IND):  Parameter I_MM_IND has value 6
MLOGIC(GET_MIN_ST_IND):  Parameter TH_PCT has value 0.1
MLOGIC(GET_MIN_ST_IND):  %LOCAL  _ST_IND_LP_OFF_MM
MLOGIC(GET_MIN_ST_IND):  %LET (variable name is _ST_IND_LP_OFF_MM)
MPRINT(GET_MIN_ST_IND):   proc sql noprint;
MPRINT(GET_MIN_ST_IND):   select mm_ind into: _t_mm_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND):   select lp_st_ind into: _t_st_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND):   select lp_end_ind into: _t_end_ind from zz500_0827_mm_ind where i=6;
MPRINT(GET_MIN_ST_IND):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.01 seconds
      cpu time            0.00 seconds


MPRINT(GET_MIN_ST_IND):   data _index_temp;
MPRINT(GET_MIN_ST_IND):   set zz500_0827_pout_wt(where=(ind between 7449 and 10747));
MPRINT(GET_MIN_ST_IND):   run;

NOTE: There were 3299 observations read from the data set WORK.ZZ500_0827_POUT_WT.
      WHERE (ind>=7449 and ind<=10747);
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 25 variables.
NOTE: DATA statement used (Total process time):
      real time           0.01 seconds
      cpu time            0.01 seconds


MPRINT(GET_MIN_ST_IND):   proc sort data=_index_temp ;
MPRINT(GET_MIN_ST_IND):   by DESCENDING ind;
MPRINT(GET_MIN_ST_IND):   run;

NOTE: There were 3299 observations read from the data set WORK._INDEX_TEMP.
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 25 variables.
NOTE: PROCEDURE SORT used (Total process time):
      real time           0.02 seconds
      cpu time            0.00 seconds


MPRINT(GET_MIN_ST_IND):  quit;
MPRINT(GET_MIN_ST_IND):   data _index_temp;
MPRINT(GET_MIN_ST_IND):   set _index_temp;
MPRINT(GET_MIN_ST_IND):   retain index_max index_off_max_max;
MPRINT(GET_MIN_ST_IND):   if _N_=1 then do;
MPRINT(GET_MIN_ST_IND):   index_max=zz500;
MPRINT(GET_MIN_ST_IND):   index_off_max_max=0;
MPRINT(GET_MIN_ST_IND):   end;
MPRINT(GET_MIN_ST_IND):   else do;
MPRINT(GET_MIN_ST_IND):   if zz500>index_max then index_max=zz500;
MPRINT(GET_MIN_ST_IND):   index_off_max=index_max-zz500;
MPRINT(GET_MIN_ST_IND):   if index_off_max>index_off_max_max then index_off_max_max=index_off_max;
MPRINT(GET_MIN_ST_IND):   end;
MPRINT(GET_MIN_ST_IND):   run;

NOTE: There were 3299 observations read from the data set WORK._INDEX_TEMP.
NOTE: The data set WORK._INDEX_TEMP has 3299 observations and 28 variables.
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds


MPRINT(GET_MIN_ST_IND):  quit;
MPRINT(GET_MIN_ST_IND):   proc sql noprint;
MPRINT(GET_MIN_ST_IND):   select ind into: _st_ind_lp_off_mm from _index_temp where
index_off_max_max<0.1 having zz500=max(zz500);
NOTE: The query requires remerging summary statistics back with the original data.
MPRINT(GET_MIN_ST_IND):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds


MPRINT(GET_MIN_ST_IND):   10295
MLOGIC(GET_MIN_ST_IND):  Ending execution.
%macro test();
	%if %checkds(_index_temp) eq 1 %then %do;
		%put "file exists";
	%end;
	%if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;
		%put "FINE";
	%end;
%mend;

%test();
25050  %macro test();
25051      %if %checkds(_index_temp) eq 1 %then %do;
25052          %put "file exists";
25053      %end;
25054      %if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;
25055          %put "FINE";
25056      %end;
25057  %mend;
25058
25059  %test();
MLOGIC(TEST):  Beginning execution.
MLOGIC(CHECKDS):  Beginning execution.
MLOGIC(CHECKDS):  Parameter DSN has value _index_temp
MLOGIC(CHECKDS):  %LOCAL  DS_EXIST
MLOGIC(CHECKDS):  %LET (variable name is DS_EXIST)
MLOGIC(CHECKDS):  %IF condition %sysfunc(exist(&dsn)) is TRUE
MLOGIC(CHECKDS):  %LET (variable name is DS_EXIST)
MLOGIC(CHECKDS):  Ending execution.
MLOGIC(TEST):  %IF condition %checkds(_index_temp) eq 1 is TRUE
MLOGIC(TEST):  %PUT "file exists"
"file exists"
MLOGIC(GET_MIN_ST_IND):  Beginning execution.
MLOGIC(GET_MIN_ST_IND):  Parameter INDEXOUT has value zz500
MLOGIC(GET_MIN_ST_IND):  Parameter DT has value 0827
MLOGIC(GET_MIN_ST_IND):  Parameter I_MM_IND has value 6
MLOGIC(GET_MIN_ST_IND):  Parameter TH_PCT has value 0.1
MLOGIC(GET_MIN_ST_IND):  %LOCAL  _ST_IND_LP_OFF_MM
MLOGIC(GET_MIN_ST_IND):  %LET (variable name is _ST_IND_LP_OFF_MM)
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand
       is required. The condition was: %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295
MLOGIC(GET_MIN_ST_IND):  Ending execution.
ERROR: The macro TEST will stop executing.
MLOGIC(TEST):  Ending execution.

1 ACCEPTED SOLUTION

Accepted Solutions
FreelanceReinh
Jade | Level 19

Hello @hellohere,

 

Your approach fails because the macro call %get_min_st_ind(...) resolves to SAS code (DATA and PROC steps), whereas your intention is that it resolves to a single value (&_st_ind_lp_off_mm), as if macro get_min_st_ind was a function-style macro.

 

I would probably move the %LOCAL statement

%local _st_ind_lp_off_mm;

to macro test, remove the macro variable reference at the end of macro get_min_st_ind and use it in the %IF condition in macro test after a macro call %get_min_st_ind(...).

 

Here is a simplified example showing this structure:

 

%macro inner(firstname);
proc sql noprint;
select age into :mvar from sashelp.class
where name="&firstname";
quit;
%mend inner;

%macro outer;
%local mvar;
%inner(Alfred)
%if &mvar=14 %then %put FINE;
%mend outer;
%outer

 

 

View solution in original post

8 REPLIES 8
FreelanceReinh
Jade | Level 19

Hello @hellohere,

 

Your approach fails because the macro call %get_min_st_ind(...) resolves to SAS code (DATA and PROC steps), whereas your intention is that it resolves to a single value (&_st_ind_lp_off_mm), as if macro get_min_st_ind was a function-style macro.

 

I would probably move the %LOCAL statement

%local _st_ind_lp_off_mm;

to macro test, remove the macro variable reference at the end of macro get_min_st_ind and use it in the %IF condition in macro test after a macro call %get_min_st_ind(...).

 

Here is a simplified example showing this structure:

 

%macro inner(firstname);
proc sql noprint;
select age into :mvar from sashelp.class
where name="&firstname";
quit;
%mend inner;

%macro outer;
%local mvar;
%inner(Alfred)
%if &mvar=14 %then %put FINE;
%mend outer;
%outer

 

 

hellohere
Pyrite | Level 9
%macro test();
	%if %checkds(_index_temp) eq 1 %then %do;
		%put "file exists";
	%end;
	%get_min_st_ind(zz500, 0827, 6, 0.1)
	%if &_st_ind_lp_off_mm. eq 10295 %then %do;
		%put "FINE";
	%end;
%mend;

%test();

I remove the Mvar at the end and define this: %global _st_ind_lp_off_mm. 

 

It surely work. I just curious why/what cause not working. 

I have the macro alike, checkds(ds), which works fine. Why not this one?! 

 

%macro checkds(dsn);
   %local ds_exist;
   %let ds_exist=0;
   %if %sysfunc(exist(&dsn)) %then
      %do;
        /*%put The data set &dsn DOES exist.;*/
		%let ds_exist=1;
      %end;
   %else
         %put The data set &dsn does NOT exist.;
   &ds_exist 
%mend ;

 

FreelanceReinh
Jade | Level 19

@hellohere wrote:

I remove the Mvar at the end and define this: %global _st_ind_lp_off_mm. 

Sure, you can also define mvar as a global macro variable. My approach was to keep it local to the calling macro.

 


@hellohere wrote:

I have the macro alike, checkds(ds), which works fine. Why not this one?! 

Macro checkds is a function-style macro. It consists of macro code only and resolves to a single value.

 

An alternative approach is to modify your macro get_min_st_ind so that it becomes a function-style macro. For example, you can "hide" the DATA and PROC steps in a %SYSFUNC(DOSUBL(...)) call:

%macro inner(firstname);
%local rc;
%let rc=%sysfunc(dosubl('proc sql noprint;
                         select age into :mvar from sashelp.class
                         where name="&firstname";
                         quit;'
                       ));
&mvar
%mend inner;

%macro outer;
%if %inner(Alfred)=14 %then %put FINE;
%mend outer;
%outer
hellohere
Pyrite | Level 9
Macro checkds is a function-style macro. It consists of macro code only and resolves to a single value. I see now.
Tom
Super User Tom
Super User

Note to make a macro that wants to return a value via a macro variable more flexible do not hardcode either %LOCAL or %GLOBAL statements.  

 

Instead have the macro check if the macro variable it wants to use to return the value exists or not and only when it does not already exist make a new GLOBAL macro variable.

 

You might even want to add a parameter to the macro so the user can tell it what macro variable name to use.

%macro mymacro(in1,in1,out=mvarname);
%if not %symexist(&out) %then %global &out ;
....
%let &out = ....
%mend mymacro;
quickbluefish
Barite | Level 11

Your first macro, %get_min_st_ind() has non-macro steps (DATA and PROC steps) inside it.  When you try to call this macro inside your logic in %test(), you are trying to run these non-macro steps inside a %if ... %then statement:

 

 

%if %get_min_st_ind(zz500, 0827, 6, 0.1) eq 10295 %then %do;

I think this may be confusing if you're coming from another language and under the impression that SAS macros are just like functions in another language, like Python or whatever.  Macros do not "return" a value in the same sense as a true function - instead, they literally write out the text generated by the macro wherever they are run, and that means that this text has to be legal at this point in the code.  

 

 

In order for the call to %get_min_st_ind() to work as you have it above, you would need to find a way to remove all non-macro steps from inside that macro (again, the PROC and DATA steps), which may or may not be possible given what you're trying to do (I really haven't looked closely at what's happening).  

 

If you can't do that, then you could instead just have that macro generate a %global macro variable (_st_ind_lp_off_mm), and then, in %test(), check the value of that global macro variable in your %if...%then... logic.  

hellohere
Pyrite | Level 9

%get_min_st_ind() has non-macro steps (DATA and PROC steps) inside it ... So the DATA/Proc are non-macro steps?! Glad to know. Thanks.

Tom
Super User Tom
Super User

Because you ran it with the MPRINT option turned on the first LOG you shared shows the problem .

MPRINT(GET_MIN_ST_IND):   proc sql noprint;

First a string like proc sql noprint is never going to the same as the digit string in your %IF statement.  And the semi-colons are going to cause all kinds of havoc also.

 

Another way to test a macro to see if might be possible to use it that way is just use a %PUT statement.

%put "%get_min_st_ind(zz500, 0827, 6, 0.1)";

 

 

 

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 8 replies
  • 255 views
  • 3 likes
  • 4 in conversation