Did you know that PROC SQL captures the record count for a result set in a special automatic macro variable? When you create a subset of data to include in a report, it's a nice touch to add a record count and other summaries as an eye-catcher to the report title. I often see the following pattern in SAS programs, which adds an extra step to get a record count:
proc sql noprint; create table result as select * from sashelp.cars where origin='Asia'; /* count the records in the result */ select count(model) into :resultcount from result; quit; title "Summary of Cars from Asia: &resultcount. records"; proc means data=result; run; |
This creates a report with an informative title like this:
Here's the tip. Instead of including a SELECT INTO step that's going to make another pass through the data, you can rely on the &SQLOBS automatic macro variable. This variable holds the record "result set" count from the most recent SELECT clause.
proc sql noprint; create table result as select * from sashelp.cars where origin='Asia'; %let resultcount=&sqlobs; quit; title "Summary of Cars from Asia: &resultcount. records"; proc means data=result; run; |
Because SAS replaces the value with each subsequent SELECT clause, it's important to assign it to another macro variable immediately if you intend to use it later. Here's the result:
Not only is this more efficient, but SAS automatically trims the whitespace from the SQLOBS variable so that it looks better in a TITLE statement. If you're using SELECT INTO to populate macro variables for other reasons, you can use the TRIMMED keyword to achieve the same effect.
proc sql noprint; create table result as select * from sashelp.cars where origin='Asia'; %let resultcount=&sqlobs; select avg(mpg_highway) into: AvgMpg TRIMMED from result; quit; title "Summary of Cars from Asia: &resultcount. records, &AvgMpg. MPG Average"; proc means data=result; run; |
See also
- See the SAS Help Center for more about how to use PROC SQL with the SAS macro language.
- DATA step has a similar macro variable, SYSNOBS. It's a little bit trickier to use reliably though, since different interactions can affect the count. Test before you use!
- SQLOBS is also handy for use in macro control loops.
8 Comments
I had never heard of "sqlobs" as an automatic macro variable and I think it can definitely help in reducing number of lines of code in a program. As we know, COUNT(*) and COUNT( variable ) may not provide the same value. "sqlobs" can be a substitute for both COUNT(*) and COUNT( variable ). I did a small test as shown below.
Thank you Chris for sharing this tip !
data example;
input num_value;
datalines;
1
2
.
4
;
/*Count will be 4*/
proc sql;
select count(*) from example;
quit;
proc sql;
select * from example;
quit;
%put Value of sqlobs is &sqlobs;
All right! Glad to know you learned something new!
use keyword _NULL_ could save resource and faster. Borrowed it from Arthur Carpenter.
proc sql noprint;
create table _null_
as select * from sashelp.cars
where origin='Asia';
%let resultcount=&sqlobs;
quit;
%put &sqlobs ;
Great tip Ksharp! I'd guess this is optimized for SAS data sets especially, as the _null_ target is special for DATA step constructs too.
not sure create table _null_ as select * from ... would be faster than
select count(*) into :resultcount from ...
I would suggest people try this both ways on some of their bigger tables.
SQLOBS is also useful when creating a series of macro var. For example:
proc sql noprint;
select distinct model
into :model1-
from sashelp.cars;
%let nummodels=&sqlobs;
Then you can write a %DO loop to loop through &nummodels as necessary to process other data using &&model&i (assuming i is your do loop var name).
This also employs the "trick" of creating a series of macro variables without knowing the ending number.
Thanks Michelle! That "open ended" range feature was added in SAS 9.3.
Human Nature is a funny thing. I implemented it originally as :model1 - :model99999 so people could "specify" roughly how many there would be. So many people were worried that model9999 would cost way more than model999 when there were only 100 or so actual records. They would run a pre-query so they could choose the useful number of 9's !!
model1 - model99999999 is practically the exact same thing as the open-ended variant added in 9.3
but it makes people so much happier not to have to guess the upper bound! who knew :-)
paul