ODS statements are global SAS statements. As such, you can put them anywhere in your SAS program. For maximum readability, many SAS programmers agree that most ODS statements should appear outside procedures in "open" SAS code. For example, most programmers agree that the following statements should appear outside of procedures:
ods graphics off; ods html style=HTMLBlue; ods listing close;
However, it is less clear whether the ODS SELECT and the ODS OUTPUT statements should appear. Should you put them outside of a procedure boundary, or inside?
I have tried to summarize both sides of the debate by using a Point/Counterpoint approach. The argument for putting ODS statements outside of procedures is summarized by Otis Outside. The argument for putting ODS statements inside procedures is summarized by Ingrid Inside. I thank participants on the SAS-L discussion forum and several colleagues at SAS for sharing their thoughts on this matter. Hopefully Ingrid and Otis represent your views fairly and faithfully.
POINT: ODS statements should appear outside procedures
By: Otis Outside
When you write a procedure statement, put your ODS SELECT and ODS OUTPUT statements outside the procedure, like this:
ods select ParameterEstimates FitStatistics; ods output ParameterEstimates=PE; proc reg data=Sashelp.Class; model weight = height age; quit;
In this program, the ODS SELECT statement is used to limit the output of the subsequent procedure call. Only the ParameterEstimates table and the FitStatistics table will be displayed. Furthermore, the ODS OUTPUT statement specifies that the ParameterEstimates table will be written to a SAS data set.
ODS statements—like all global statements—should always appear outside of procedures. The only statements that should appear inside a procedure are the statements that apply to that procedure. (Notice that if you look at the procedure documentation, you will not find the global statements there!) If you put the TITLE statement and all ODS statements before the PROC statement, it is clear that they are global statements that will affect the subsequent procedure call.
Furthermore, in procedures that support an OUTPUT statement, it is potentially confusing to put an ODS OUTPUT statement inside the procedure because it looks so similar to the OUTPUT statement. Eliminate the possible confusion by putting the ODS OUTPUT statement outside the procedure.
Although Rick has described an example in which an ODS statement does not work when it follows an interactive procedure, this argument is nullified if programmers correctly end interactive procedures by using a QUIT statement. Programmers like me should not have to modify a logical programming convention just to protect against the possibility that someone else might not terminate a procedure correctly.
COUNTERPOINT: ODS statements should appear inside procedures
By: Ingrid Inside
When you write a procedure statement, put your ODS SELECT and ODS OUTPUT statements inside the procedure, like this:
proc reg data=Sashelp.Class; model weight = height age; ods select FitStatistics; run; model weight = height; ods select ParameterEstimates; ods output ParameterEstimates=PE; run; quit;
The REG procedure is an interactive procedure. Every time it encounters a RUN statement, it processes the preceding statements and displays the results. When it terminates with the QUIT statement, any ODS OUTPUT data sets are closed. In the preceding statements, the FitStatistics table is displayed for the first model that is specified. The second MODEL statement changes the model. For the second model, the ParameterEstimates table AND the FitStatistics table are both displayed because the ODS SELECT statement acts cumulatively. However, the ODS OUTPUT statement creates a data set that contains only the parameter estimates for the second model.
You can't get this flexibility if you put the ODS statements outside of the procedure. To obtain the full power of the ODS system and interactive procedures, you should put the ODS statements inside the procedure. And once you start doing it for interactive procedures, it makes sense to maintain that convention for all procedures.
And I don't buy the argument that "it might be confusing" to put ODS statements inside a procedure. I think the opposite is true. An ODS statement doesn't actually do anything. It just sets up a condition in ODS. ODS then waits for the right procedure to come along and consume it. I think it is less confusing to put ODS statements inside the procedure that they affect. That's why I recommend that you use this convention for all procedures, not just the interactive procedures.
Lastly, Rick's example shows how you can get in trouble if you put ODS statements outside of a procedure. A good programmer practices defensive programming. By putting ODS statements inside of procedures, my programs are more robust.
What do you say, readers? Do you agree with Otis or with Ingrid? What convention do you use for ODS SELECT and ODS OUTPUT statements? Why? Leave a comment.