Search code examples
sascallsas-macro

Why does my macro behave differently with call execute()?


Using SAS, I often want to perform an action on each row of a dataset. To do so, I use a command I found on a tutorial : call execute(). As I'm not very familiar with SAS environment, I tend to use macro-functions to do anything I don't know how to and execute them with call execute(). However I have difficulties understanding how macro-language works exactly in SAS. I know that all macro references are resolved first, which provides a base-SAS code which is then executed (or am I already wrong ?). But I don't understand how it applies with call execute(). Let's consider the following code :

%macro prog1;    /* %prog1 defines a macrovariable mv1 */
%global mv1;
data _null_;
call symputx("mv1","plop");
run;
%mend;

%macro prog2(var);    /* prog2 puts it on the log */
%put PUT &var;
%mend;

%macro prog_glob;    /* prog_glob executes prog 1 then prog2 */
%prog1;
%prog2(&mv1);
%mend;

I know there is no need for three macros here but this is a minimal version of my real code, which has this structure.

Now if I execute prog_glob :

%prog_glob;

I get PUT plop on the log, as expected. But if I use it with call execute() (even if there is no need for loop here) :

data _null_;
mac=%NRSTR("%prog_glob");
call execute(mac);
run;

I get only PUT. There is no error suggesting that the macrovariable is not defined so the %global statement worked. But somehow, prog2 was executed before prog1 base part (at least I think so) and mv1 is not defined yet.

My questions are :

  • Is my interpretation correct ?
  • Why does the result change when I use call execute ?
  • Depending on the precedent question answer, how should I fix it or is there a more convenient way to loop trough a column values ?

EDIT : My original code intends to rename the variables of several tables, which I have listed in a dataset. For each listed table, I want the following algorithm executed :

  • prog1 : store a list with all variables in a macrovariable (this is where I define mv equivalent)
  • prog2 : add a common suffix to these variables names

There is probably a more clever way to do this. Again, I'm not so familiar with SAS and I tend to over-use macros. If you want to show me a better way to do this, I'd be happy to chat but I don't expect you guys to rewrite all my code so an alternative to call execute would be enough for me to be grateful ! :)


Solution

  • Let's have a look at the documentation http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a000543697.htm

    If an EXECUTE routine argument is a macro invocation or resolves to one, the macro executes immediately. However, any SAS statements produced by the EXECUTE routine do not execute until after the step boundary has been passed.

    Note: Because macro references execute immediately and SAS statements do not execute until after a step boundary, you cannot use CALL EXECUTE to invoke a macro that contains references for macro variables that are created by CALL SYMPUT in that macro. See Interfaces with the Macro Facility, for an example.

    This means, if you call it via call execute:

    1. macro statements are executed immediately - those are:

      1.1. first in %prog1: %global mv1; - so mv1 is defined but empty, no Apparent... warning

      1.2. SAS statements from %prog1 are still deferred

    2. Now %prog2 - here's only macro statement %PUT which puts (still empty) &mv1 variable. That what you see in the log
    3. Now all the what gets executed immediately has been done. The data step which contains call execute ends.
    4. SAS statements deferred from call execute are now being executed:

      4.1. the dataset from prog1 sets the value for mv1.

    And that's all :-)

    EDIT: regarding your edit: try looking at this http://support.sas.com/kb/48/674.html