BookmarkSubscribeRSS Feed
Anv_SAS
Fluorite | Level 6
proc sort data=sashelp.class out=class0 nodupkey;
by age;
run;
 
data class0;
set class0;
n=_n_;
run;
 
proc sort data=sashelp.class out=class;
by age;
run;
 
data class;
merge class(in=a) class0(in=b keep=age n);
by age;
if a;
if sex="F" then m=1;
if sex="M" then m=2;
run;
 
%macro chk;
 
proc sql feedback noprint;
%do i=1 %to 5;
%do j=1 %to 2;
select  distinct age into :age&i.&j. from class where n=&i. and m=&j.;
%end;
%end;
quit;
%mend;
%chk;
 
%put &age11.;
 
When I ran above code I am getting warning "WARNING: Apparent symbolic reference AGE11 not resolved.". Please help in fixing this issue
9 REPLIES 9
PaigeMiller
Diamond | Level 26

Macro variables created inside a macro can only be accessed within that macro (or by macros called by this macro). These macro variables do not have a value in open code (which is code outside of any macro).

 

You can fix this by adding a %GLOBAL statement into your macro.

 

%global age11;

 

By the way, I realize that this is probably just an example to learn macro code, but you really should never need to partition a data set this way by using macro loops, or extract information from a data set by using macro loops. There are better tools that are also easier to program with. Part of learning to use macros is learning when to NOT use macros.

--
Paige Miller
Tom
Super User Tom
Super User

Move the %PUT statement to inside the macro itself and you should see the value of the macro variable.

 

If you need the macro variables to continue to exist after the macro has finished running then you need to either  create them before calling the macro or tell SAS make them in the GLOBAL symbol table instead of the symbol table of the currently running macro.

 

If you want to use SQL then add code to make the macro variable global when it does not yet exist.

%macro chk;
%local i j; 
proc sql feedback noprint;
%do i=1 %to 5;
  %do j=1 %to 2;
    %if not %symexist(age&i.&j.) %then %global age&i.&j. ;
select distinct age into :age&i.&j. from class where n=&i. and m=&j.;
  %end;
%end;
quit;
%mend;

Note the DISTINCT is not needed here since you did not include a SEPARTED BY clause the INTO will only put the first value of AGE into the macro variable.

 

If you switch to using a DATA step instead then you can use CALL SYMPUTX() which will allow to write directly into the GLOBAL symbol table.

data _null_;
  set class;
  where n in (1:5) and m in (1:2);
  call symputx(cats('age',n,m),age,'G');
run;

But since with the data step code there is no need for %DO loops you probably do not even need to define the CHK macro.  Which means you don't need to worry as much about where the macro variables are created.

dxiao2017
Lapis Lazuli | Level 10

Hi @Anv_SAS , I think this is what you wanted to achieve by the code you provided, however, you did not get what you desired. Here is my suggestion, please kindly let me know if it answer your question.

 

(1) you applied categories to the age and sex variables through the proc sort and merge steps, the code and output are as follows (in the output table, column n is age category and column m is sex category):

proc sort data=sashelp.class 
          out=class0 nodupkey;
   by age;
run;
data class0;
   set class0;
   n=_n_;
run;
proc print data=class0;title "class0";run;
proc sort data=sashelp.class 
          out=class;
   by age;
run;
proc print data=class;title "class";run;
data class2;
   merge class(in=a) 
         class0(in=b keep=age n);
   by age;
   if a;
   if sex="F" then m=1;
   if sex="M" then m=2;
run;
proc print data=class2;title "class2";run;

dxiao2017_0-1753952139116.png

dxiao2017_1-1753952201508.png

 

(2) you were going to produce a macro variable which is the combination of the age and sex categories, and this macro variable resolves to respective students' names in the class as its' macro variable values. However, you did not choose the simple and efficient method to do this (you applied many techniques including macro, sql, nested do loop, which made the code difficult to debug). Here is my thinking and suggestion: you can simply use a single sql or data _null_  step to produce this macro variable.

proc sql;
select name,n,m
   into :name1-,:n1-,:m1-
   from class2;
quit;
%put _user_;

data _null_;
   set class2;
   call symputx(name,cat(n,m));
run;
%put _user_;

data _null_;
   set class2;
   call symputx(cat('age',n,m),name);
run;
%put _user_;

dxiao2017_2-1753952856091.png

86         %put _user_;
 GLOBAL AGE11 Joyce
 GLOBAL AGE12 Thomas
 GLOBAL AGE21 Louise
 GLOBAL AGE22 Robert
 GLOBAL AGE31 Barbara
 GLOBAL AGE32 Jeffrey
 GLOBAL AGE41 Judy
 GLOBAL AGE42 Henry
 GLOBAL AGE51 Mary
 GLOBAL AGE52 William
 GLOBAL AGE62 Philip
 GLOBAL ALFRED 42
 GLOBAL ALICE 31
 GLOBAL BARBARA 31
 GLOBAL CAROL 41
 GLOBAL HENRY 42
 GLOBAL JAMES 22
 GLOBAL JANE 21
 GLOBAL JANET 51
 GLOBAL JEFFREY 32
 GLOBAL JOHN 22
 GLOBAL JOYCE 11
 GLOBAL JUDY 41
 GLOBAL LOUISE 21
 GLOBAL M1 1
 GLOBAL M10 2
 GLOBAL M11 2
 GLOBAL M12 1
 GLOBAL M13 2
 GLOBAL M14 1
 GLOBAL M15 1
 GLOBAL M16 1
 GLOBAL M17 2
 GLOBAL M18 2
 GLOBAL M19 2
 GLOBAL M2 2
 GLOBAL M3 2
 GLOBAL M4 1
 GLOBAL M5 2
 GLOBAL M6 1
 GLOBAL M7 2
 GLOBAL M8 1
 GLOBAL M9 1
 GLOBAL MARY 51
 GLOBAL N1 1
 GLOBAL N10 3
 GLOBAL N11 4
 GLOBAL N12 4
 GLOBAL N13 4
 GLOBAL N14 4
 GLOBAL N15 5
 GLOBAL N16 5
 GLOBAL N17 5
 GLOBAL N18 5
 GLOBAL N19 6
 GLOBAL N2 1
 GLOBAL N3 2
 GLOBAL N4 2
 GLOBAL N5 2
 GLOBAL N6 2
 GLOBAL N7 2
 GLOBAL N8 3
 GLOBAL N9 3
 GLOBAL NAME1 Joyce
 GLOBAL NAME10 Jeffrey
 GLOBAL NAME11 Alfred
 GLOBAL NAME12 Carol
 GLOBAL NAME13 Henry
 GLOBAL NAME14 Judy
 GLOBAL NAME15 Janet
 GLOBAL NAME16 Mary
 GLOBAL NAME17 Ronald
 GLOBAL NAME18 William
 GLOBAL NAME19 Philip
 GLOBAL NAME2 Thomas
 GLOBAL NAME3 James
 GLOBAL NAME4 Jane
 GLOBAL NAME5 John
 GLOBAL NAME6 Louise
 GLOBAL NAME7 Robert
 GLOBAL NAME8 Alice
 GLOBAL NAME9 Barbara
 GLOBAL PHILIP 62
 GLOBAL ROBERT 22
 GLOBAL RONALD 52

 

(3) to conclude, the last data _null_ step might be the solution, which produces desired result and the macro variable &age11 was correctly resolved to a class student's name, Joyce, the code and results is as follows.

data _null_;
   set class2;
   call symputx(cat('age',n,m),name);
run;
%put &age11;
69         data _null_;
 70            set class2;
 71            call symputx(cat('age',n,m),name);
 72         run;

 NOTE: There were 19 observations read from the data set WORK.CLASS2.

 73         %put &age11;
 Joyce

 

Anv_SAS
Fluorite | Level 6

Thanks all, for helping in fixing the issue. As mentioned by Paige Miller, I have added %global statement and issue fixed. Also I have deleted those global macro variables using %symdel statement. My requirement is to pull pool of records into a single macro variable conditionally. And finally chopping them into individual records again using SCAN. To avoid overwriting of original files, I used SASHELP.ClASS to test the logic. As Paige Miller suggested we have different non-macro methods as well which can fit in.  Once again Thanks all for your valuable solutions 🙂

PaigeMiller
Diamond | Level 26

My requirement is to pull pool of records into a single macro variable conditionally.

 

Data from several (many) records does not belong in a macro variable. It belongs in a SAS data set. Please explain your project in more global terms, do not talk about SAS code, but do talk about what the data is and how will you use of the data once you pull it.

--
Paige Miller
Anv_SAS
Fluorite | Level 6

Hi Paige Miller, I work on a study and I was instructed to generate Cartesian Product from 2 datasets applying 2 filter conditions, to ensure the respective records only needs to be used. Also I was instructed not to use direct Cartesianing techniques like PROC SQL or DATA step POINT statement. I tried this using PROC SQL macro variables creation method (PROC SQL; select records into :rec_&i.&j from <dataset> where <var1>=1 and <var2>=1;quit ). However, we have many values in 2 filter variables, due to which I need to write many PROC SQL steps in OPEN code. So I thought I could create a macro to iterate the nested loop.

 

%macro rec_list;

%do i=1 %to 3;

%do j=1 %to 4;

 proc sql;

select records into :rec_list_&i.&j. seperated by " " from ABC where VAR1=&i AND VAR2=&j;

select count(*) into :n_list_&i.&j.  from ABC where VAR1=&i AND VAR2=&j;

quit;

%end;

%end;

%mend;

 

%rec_list;

%put &rec_list_11.;

 

%macro loop (j=, outds=, v1=, v2=);
 
data a11&v1.&v2.;
set a1;
if var1=&v1. and var2=&v2. ;
run;
 
data a_&outds.;
     set a11&v1.&v2.;
      %do i=1 %to &&n_&outds.;
     %let rec=%scan(&&rec_list&outds.., &i, 1234567890-,k);
     record="&rec.";
     output;
     %end;
run;
%mend loop;
 
%loop (j=1, outds=11, v1=1, v2=1); %loop (j=1, outds=12, v1=1, v2=2);

 

On running above code macro variables are generated but I could not see macro variables resolving. 

 

After I use %global statement, I can see macro variables resolving and I deleted them using %symdel at last of the pgm to avoid confusion or overwriting of Global Macro variables. 

PaigeMiller
Diamond | Level 26

@Anv_SAS wrote:

Hi Paige Miller, I work on a study and I was instructed to generate Cartesian Product from 2 datasets applying 2 filter conditions, to ensure the respective records only needs to be used. Also I was instructed not to use direct Cartesianing techniques like PROC SQL or DATA step POINT statement.


Okay. Thank your for the explanation. I feel sorry for you, that you have been told to do something but you can't use tools specifically designed to perform the desired task. Its like telling a carpenter that he can't use a saw or telling a plumber he can't use a wrench.

 

Still, it is highly recommended that you mention the goal at the beginning (for this thread and for all future threads) instead of not mentioning it. Perhaps if people knew you were trying to create Cartesian product with macro variables, a brilliant idea would pop into someone's head that would get you there quickly (and there are a lot of people who do have brilliant ideas in this forum).

 

Anyway, I will drop out of this discussion now, as I do not care to hurt my brain creating Cartesian products using macro variables. Best wishes and good luck.

--
Paige Miller
Tom
Super User Tom
Super User

Why did you write it as two separate macros?  If you made the macro variables inside the same macro that used them you could instead make them LOCAL and not have to worry about deleting them since they would disappear when the macro finished.

andreas_lds
Jade | Level 19

The issue that should be fixed is the creation of 10 macro variables. 

So: what do you want to do after macro chk?

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
  • 9 replies
  • 934 views
  • 10 likes
  • 5 in conversation