The following example is very simple and might be solvable in an easier way. However, I am interested in making it work. The following example is based on the cars-dataset of the sashelp-library. First, I have a macro called fun:
proc contents data = sashelp.cars out = mycontents;
run;
%macro fun(var);
proc sql noprint;
select count(distinct(&var.))
into :obs
from sashelp.cars;
quit;
%mend;
Now I want to call the macro but only to update obs (from the input statement). I use:
data work.test;
set mycontents;
if name ne "Type" then do;
call execute('%nrstr(%fun('||name||');');
new = &obs;
end;
else new = 5;
run;
In short, this should iterate the rows of the mycontents. Then call one (of several) macros depending on name, which updates obs. Then I can simply populate the new column new with obs. However, obs stays the same value for all names, which is the value from the last variable.
Here the problem is twofold.
First, you cannot use
CALL EXECUTE
in this context because that doesn't execute until after the data step has completed running: so anything depending on&obs
will not be able to get an updated value for that. You'll have to usedosubl
.Second, you need to use
symget('obs')
, not&obs
, if you want to get an updated value mid-data step.&obs
will resolve when the data step is compiled, so it cannot change during execution; butsymget(obs)
instructs the data step to query the symbol table during execution.Here's an example of doing this with
dosubl
, minimally changed from your example. Note the%global
statement to make sureobs
is available to us in the data step (there are other ways to do this to get it back that are better - namely, wrapping this in afcmp
function and usingrun_macro
- but this is closest to how you do it).