BookmarkSubscribeRSS Feed
PeterPZC
Fluorite | Level 6

Hi all,

@I'm pretty green to SAS and SAS EG. I try to define macro within macro but turns out I got the problem as described in 'MACRO within a MACRO problems' MACRO within a MACRO problems. , that no lines underneath the MEND statement, which means SAS enhanced editor fail to identify the code block. 

 

I understand that it's a bad practice to write macro within macro but I'm doing so to reduce the numbers of codes as I need to create a lot of similar subqueries that is having the same select and joining clause(or same clause applying to different tables, the idea is that the code are similar and I want to write them in one macro).

 

Is there a way to tell the SAS editor where the code ends so that it can be collapsed for easier navigation?

 

Or what is the correct way to write macro within macro so that I won't encounter this problem?

 

 

My high level example code is as follow:

 

%macro M1(code);

 

%if code=1 %then %do;

%macro whereclause();

Where t1.c3=A1 and t1.c4=B1

%mend whereclause();

 

%macro groupclause();

Group by t1.c3, t1.c4 

%mend groupclause();

 

%end;

 

%if code=2 %then %do;

%macro whereclause();

Where t1.c3=A2 and t1.c4=B2 and t1.c4=D2  

%mend whereclause();

 

%macro groupclause();

Group by t1.c3, t1.c4, t1.c5 

%mend groupclause();

 

%end;

.

.

.

 

(select t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, max(t1.c2) as c6 

from t1 inner t2 on t1.c1=t2.c1 and t1.c2=t2.c2

%whereclause()

%groupclause() 

)

%mend M1;

 

 

 

Thanks and regards,

Peter

16 REPLIES 16
Reeza
Super User

There’s a reason to not nest macros. Explain what you’d like to do overall and many people will offer solutions and options but nesting macros is a bad idea.

 


@PeterPZC wrote:

Hi all,

@I'm pretty green to SAS and SAS EG. I try to define macro within macro but turns out I got the problem as described in 'MACRO within a MACRO problems' MACRO within a MACRO problems. , that no lines underneath the MEND statement, which means SAS enhanced editor fail to identify the code block. 

 

I understand that it's a bad practice to write macro within macro but I'm doing so to reduce the numbers of codes as I need to create a lot of similar subqueries that is having the same select and joining clause(or same clause applying to different tables, the idea is that the code are similar and I want to write them in one macro).

 

Is there a way to tell the SAS editor where the code ends so that it can be collapsed for easier navigation?

 

Or what is the correct way to write macro within macro so that I won't encounter this problem?

 

 

My high level example code is as follow:

 

%macro M1(code);

 

%if code=1 %then %do;

%macro whereclause();

Where t1.c3=A1 and t1.c4=B1

%mend whereclause();

 

%macro groupclause();

Group by t1.c3, t1.c4 

%mend groupclause();

 

%end;

 

%if code=2 %then %do;

%macro whereclause();

Where t1.c3=A2 and t1.c4=B2 and t1.c4=D2  

%mend whereclause();

 

%macro groupclause();

Group by t1.c3, t1.c4, t1.c5 

%mend groupclause();

 

%end;

.

.

.

 

(select t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, max(t1.c2) as c6 

from t1 inner t2 on t1.c1=t2.c1 and t1.c2=t2.c2

%whereclause()

%groupclause() 

)

%mend M1;

 

 

 

Thanks and regards,

Peter


 

PeterPZC
Fluorite | Level 6

I'm doing this for a code migration project from Access to SAS. In Access, the code is written in separate queries with specific ID to keep track. They are calling one and another just like the code that I wrote for demo. I would like to mimic the way they were written in Access but i don't want to create so many temp table as in Access so I'm using multiple nested macro.  

Tom
Super User Tom
Super User

@PeterPZC wrote:

I'm doing this for a code migration project from Access to SAS. In Access, the code is written in separate queries with specific ID to keep track. They are calling one and another just like the code that I wrote for demo. I would like to mimic the way they were written in Access but i don't want to create so many temp table as in Access so I'm using multiple nested macro.  


The number of temporary datasets you create has nothing to do with whether your code has nested macro definitions, nested macro executions, or even uses any macros at all.  Instead that is a design decision on the code you want to run, however the code ends up being generated.

jimbarbour
Meteorite | Level 14

Peter,

 

There's no benefit to placing one macro definition within another macro.  You can place a call to another macro from within a macro definition, but not another definition.  

 

Take a look at the following code.  Please copy this into a SAS editor window (or Jupyter notebook) and run it.  Please tell me the difference in the results between having one macro definition inside another vs. two separate macro definitions.

 

Jim

 

**	Bad code.  Do NOT do it!	**;
%Macro	Mac1;
	%PUT	NOTE:  Hello I am Mac1;

	%Macro	Mac2;
		%PUT	NOTE-  And I am Mac2;
	%Mend	Mac2;

	%Mac2;
%Mend	Mac1;

%Mac1;

**------------------------------------------------------------------------------**;

**	Good code.  Same result.  Do it!	**;
%Macro	Mac1;
	%PUT	NOTE:  Hello I am Mac1;
	%Mac2;
%Mend	Mac1;

%Macro	Mac2;
	%PUT	NOTE-  And I am Mac2;
%Mend	Mac2;

%Mac1;
PeterPZC
Fluorite | Level 6
I want to do nested macro because i'm actually creating similar tables (select from with different where clause/group clause etc.) I would like to create macro that takes ID as input and hence they can create slightly different tables based on the input. It's like making lego.
ChrisNZ
Tourmaline | Level 20

Don't pay too much attention to the editor's parser. While it's useful when it works, it's not very clever.

Your code however is bad practice, and also erroneous. It cannot possibly run as expected.

Try this:


%macro M1(code);

  %local grp wh;

  %if &code=1 %then %do;
    %let wh =t1.c3=A1 and t1.c4=B1;
    %let grp=t1.c3, t1.c4 ;
  %end;

  %if &code=2 %then %do;
    %let wh =t1.c3=A2 and t1.c4=B2 and t1.c4=D2  ;
    %let grp=t1.c3, t1.c4, t1.c5 ;
  %end;

  .
  .
  .
   

  (select t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, max(t1.c2) as c6 

  from t1 inner t2 on t1.c1=t2.c1 and t1.c2=t2.c2

  where &wh

  group by &grp 

  )

%mend M1;

 

 

PeterPZC
Fluorite | Level 6
Thanks Chris, actually my way of writing do run as expected but the project ceased due to poor performance of SAS comparing to Access. I don't know why, maybe looping is faster in Access than SAS? Anyway, I will try you way next time (hopefully not, as everyone said, it's bad practice)!
Tom
Super User Tom
Super User

Perhaps it is the looping that is the issue, not SAS. "Looping" not a normal practice for data analysis. Usually you just code what you want to happen for all observations and let it rip.

Tom
Super User Tom
Super User

Looks to me like your "nested" macro definitions should just be macro variables instead.

%macro M1(code);
%local whereclause groupclause;
%if &code=1 %then %do;
 %let whereclause=Where t1.c3=A1 and t1.c4=B1;
  %let groupclause=Group by t1.c3, t1.c4 ;
%end;
%else if &code=2 %then %do;
...
%end;
...
(select t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, max(t1.c2) as c6 
 from t1 inner t2 on t1.c1=t2.c1 and t1.c2=t2.c2
 &whereclause
 &groupclause
)
...
%mend M1;
PeterPZC
Fluorite | Level 6
In my case, yes. But what if the code defined in nested macro is more complicated (like a subquery, "fromclause .....") and cannot be defined as macro variable?
Tom
Super User Tom
Super User

@PeterPZC wrote:
In my case, yes. But what if the code defined in nested macro is more complicated (like a subquery, "fromclause .....") and cannot be defined as macro variable?

All macro code and macro variables generate is text to pass onto to SAS itself to be interpreted code.  

Are you saying that some of those macro generated more than 65K bytes of code?

Or are you saying that the macro generates multiple SAS statements?  There are ways to store multiple statements into a macro variable, you just need to add some macro quoting around the semi-colons and then you can use %unquote(&mvar) to remove the macro quoting and let SAS process the generated code.

PeterPZC
Fluorite | Level 6
I see, but it's quite complicated.... need to mask those characters and will make the code hard to read.... Why can't we just use nested macro instead?
Tom
Super User Tom
Super User

You can do it, but I suspect it will just make your code harder to understand and manage than if you approached to problem in a more direct way.  

 

Note that the name space for macros is flat, there is no concept of LOCAL macro definition like there is for macro variables, so the potential for name collision is much greater and harder to manage.

PeterPZC
Fluorite | Level 6
Thanks Tom, for this project, since there is a large amount of code to be transferred from Access sql to SAS, I think it will take less time to do it if i avoid the masking. Btw, may I know what do you mean by name space is flat?

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
  • 16 replies
  • 2991 views
  • 1 like
  • 5 in conversation