Using %IF-%THEN-%ELSE in SAS programs

16

SAS programmers have long wanted the ability to control the flow of their SAS programs without having to resort to complex SAS macro programming. With SAS 9.4 Maintenance 5, it's now supported! You can now use %IF-%THEN-%ELSE constructs in open code. This is big news -- even if it only recently came to light on SAS Support Communities. (Thanks to Super User Tom for asking about it.)

Prior to this change, if you wanted to check a condition -- say, whether a data set exists -- before running a PROC, you had to code it within a macro routine. It would look something like this:

/* capture conditional logic in macro */
%macro SummarizeIfExists();
 %if %sysfunc(exist(work.result)) %then
  %do;
    proc means data=work.result;
    run;
  %end; 
%else
  %do;
    %PUT WARNING: Missing WORK.RESULT - report process skipped.;
  %end;
%mend;
 
/* call the macro */
%SummarizeIfExists();

Now you can simplify this code to remove the %MACRO/%MEND wrapper and the macro call:

/* If a file exists, take an action */
/* else fail gracefully */
%if %sysfunc(exist(work.result)) %then
  %do;
    proc means data=work.result;
    run;
  %end;
%else
  %do;
    %PUT WARNING: Missing WORK.RESULT - report process skipped.;
  %end;

Here are some additional ideas for how to use this feature. I'm sure you'll be able to think of many more!

Run "debug-level" code only when in debug mode

When developing your code, it's now easier to leave debugging statements in and turn them on with a simple flag.

/* Conditionally produce debugging information */
%let _DEBUG = 0; /* set to 1 for debugging */
%if &_DEBUG. %then
  %do;
    proc print data=sashelp.class(obs=10);
    run;
  %end;

If you have code that's under construction and should never be run while you work on other parts of your program, you can now "IF 0" out the entire block. As a longtime C and C++ programmer, this reminds me of the "#if 0 / #endif" preprocessor directives as an alternative for commenting out blocks of code. Glad to see this in SAS!

/* skip processing of blocks of code */
/* like #if 0 / #endif in C/C++      */
%if 0 %then
  %do;
    proc ToBeDetermined;
      READMYMIND = Yes;
    run;
  %end;

Run code only on a certain day of the week

I have batch jobs that run daily, but that send e-mail to people only one day per week. Now this is easier to express inline with conditional logic.

/*If it's Monday, send a weekly report by email */
%if %sysfunc(today(),weekday1.)=2 %then
  %do;
    options emailsys=smtp emailhost=myhost.company.com;
    filename output email
      subject = "Weekly report for &SYSDATE."
      from = "SAS Dummy <sasdummy@sas.com>"
      to = "knowledgethirster@curious.net"
      ct ='text/html';
 
  ods tagsets.msoffice2k(id=email) 
    file=OUTPUT(title="Important Report!")
    style=seaside;
   title "The Weekly Buzz";
   proc print data=amazing.data;
   run;
  ods tagsets.msoffice2k(id=email) close;
  %end;

Check a system environment variable before running code

For batch jobs especially, system environment variables can be a rich source of information about the conditions under which your code is running. You can glean user ID information, path settings, network settings, and so much more. If your SAS program needs to pick up cues from the running environment, this is a useful method to accomplish that.

/* Check for system environment vars before running code */
%if %sysfunc(sysexist(ORACLE_HOME)) %then
  %do;
    %put NOTE: ORACLE client is installed.;
    /* assign an Oracle library */
    libname ora oracle path=corp schema=alldata authdomain=oracle;
  %end;

Limitations of %IF/%THEN in open code

As awesome as this feature is, there are a few rules that apply to the use of the construct in open code. These are different from what's allowed within a %MACRO wrapper.

First rule: your %IF/%THEN must be followed by a %DO/%END block for the statements that you want to conditionally execute. The same is true for any statements that follow the optional %ELSE branch of the condition.

And second: no nesting of multiple %IF/%THEN constructs in open code. If you need that flexibility, you can do that within a %MACRO wrapper instead.

And remember, this works only in SAS 9.4 Maintenance 5 and later. That includes the most recent release of SAS University Edition, so if you don't have the latest SAS release in your workplace, this gives you a way to kick the tires on this feature if you can't wait to try it.

Share

About Author

Chris Hemedinger

Senior Manager, SAS Online Communities

+Chris Hemedinger is the manager of SAS Online Communities. Since 1993, Chris has worked for SAS as an author, a software developer, an R&D manager and a consultant. Inexplicably, Chris is still coasting on the limited fame he earned as an author of SAS For Dummies.  He also hosts the SAS Tech Talk webcasts each year from SAS Global Forum, connecting viewers with smart people from SAS R&D and the impressive work that they do.

Related Posts

16 Comments

    • Chris Hemedinger
      Chris Hemedinger on

      I'm not sure what the roadmap is for %DO and nested %IF/%THEN/%ELSE -- but I'll try to be satisfied with this new feature for now!

  1. Don Henderson on

    Very nice. I've always been a fan of leaving debugging code in my programs. Even used _debug as the macro variable name to control that. This addition to M5 makes it orders of magnitude easier.

    Looking forward to anything you can report on the future roadmap.

  2. Michelle Buchecker on

    %sysfunc(today(),weekday1.)=2 any reason for this instead of the more direct &sysday=Monday ?

    Cool to know about that %IF though.

  3. Interesting!

    Chris, does code executed this way show up in the log as normal source code or do you need OPTIONS MPRINT to see it?

    • Chris Hemedinger
      Chris Hemedinger on

      Good question Matthew! From my tests, all of the source code shows in the log even without MPRINT or MLOGIC. But of course, only the code that actually executes would produce additional log output.

    • Prashant Chegoor on

      Also, the MLOGIC Option does NOT show any execution trace information in the SAS log for the %IF %THEN statement when used in Open Code. The trace Information is only displayed when the statement is contained within a Macro definition.

  4. Look, this is cool, and a welcome addition to SAS 9.4M5.

    But am I the only one who feels like this is around 20 years late? Are we so excited about this simply because the SAS base language has lacked modern programming constructs for so long?

    What I think would be even cooler is something like (it's just an illustration, to convey a concept - don't take it literally as the final syntax):

    function foo(a,b);
       return a**b;
    endfunc;  * or whatever... {} would also work, but would not be "sas-onic" ("pythonic") ;
    
    data foo;
       do x=1 to 10;
          do y=1 to 5;
             z=foo(x,y);  /* not proc fcmp...a "proper" support of open code function definitions */
          end
       end;
    run;
    
    if foo.nobs = 0 then do;
       print "foo is empty";
    end;
    
    do name="class","stocks","shoes";
       data.output=catx(".","work",name);
          set.input=catx(".","sashelp",name);
       run;
    end;   
    

    Instead, we still have a "C preprocessor" (https://en.wikipedia.org/wiki/C_preprocessor) (ok, a C preprocessor on steroids) to control what gets sent to the SAS compiler, instead of more modern programming constructs like ***open code*** functions, if statements, do & while loops, properties and methods, etc.

    The SAS data step language is great for data processing - I know it and use it every day. But it feels "tired" to me, compared to when I use Python, Java, C#, Powershell, and other modern programming languages.

    I know SAS is married to the idea that 40 year old code still runs, but I wish there was something like:

    options saslang=newsas;
    fancy new modern SAS language;
    
    options saslang=oldsas;
    40 year old SAS language;
    

    Or perhaps just two different installs ("old SAS", "new SAS"), if the above would bloat SAS too much to have both code bases in the same application.

    (I hope I don't get flamed too badly for sharing these thoughts ;-) )

    • Chris Hemedinger
      Chris Hemedinger on

      No flames from me.

      The SAS macro language has been the "control" language of SAS for decades, and those proficient in SAS macro have accomplished amazing feats of reuse and control. Of course it predates many of the other languages you mention, so the syntax has a learning curve. Also, projects like PROC LUA and the saskernel (Jupyter) and SASPy are designed to allow you to mix your "other favorite" languages with SAS.

      As far as new innovations and "new SAS", you'll find a ton of new syntax in SAS Viya/CAS. In addition to some modern constructs (action sets), the language is optimized for parallel processing and in-memory computation. See this paper for an introduction.

  5. Suryakiran Pothuraju on

    This is really a good news. Most recently I used macro routine with %IF-%THEN %ELSE to check if a macro variable exists or not. If exists I need to run one program and if not then other program. This new feature will simplify my code and also what I noticed in SAS EG is the color for the syntax will be maintained in open code, where as in between %MACRO - %MEND will be plain.

    %macro check();
    %if %symexist(name) %then %do;
    /* Proc sql code here is plain */
    proc sql;
    select * from sashelp.class
    where name in (&name);
    quit;
    %end;
    %mend check;
    %check;

    %if %symexist(name) %then %do;
    /* Proc sql code here maintains syntax color */
    proc sql;
    select * from sashelp.class
    where name in (&name);
    quit;
    %end;

  6. Peter Lancashire on

    Thanks for this. It is never too late. My long-time partial workaround has been a %do_if() macro which evaluates the parameter and returns * if false, nothing if true. Example:

    .
    %do_if(&DEBUG) %include "debug-code.sas";
    .
    

    Question: does this syntax work:
    .
    %if &MYCONDITION %then %do;
    ... some code ...
    %end;
    

    • Chris Hemedinger
      Chris Hemedinger on

      Peter,

      Yes, that works as long as &MYCONDITION is defined as a numeric value. 0 is "false", any other number is "true". If &MYCONDITION is undefined as a macro symbol, or is assigned as any other type (character string), I think you'll see an error in the log. And if you try something tricky like:

      %if %symexist(mycondition) AND &MYCONDITION %then %do;
       %put Running this!;
      %end;
      

      ...you'll still see an error if &MYCONDITION is not defined. Looks like SAS will try (and fail) to resolve the macro value even if you attempt to short-circuit the check with a more complex boolean. So if you absolutely need this check, you would need to guarantee a defined value for the macro var.
      /* in case some other process did not yet define this macro var */
      %if %symexist(mycondition)=0 %then %do;
       %let mycondition = 0;
      %end;
      %if  &MYCONDITION %then %do;
       %put Running this!;
      %end;
      

Leave A Reply

Back to Top