LISTSERV at the University of Georgia
Menubar Imagemap
Home Browse Manage Request Manuals Register
Previous messageNext messagePrevious in topicNext in topicPrevious by same authorNext by same authorPrevious page (August 2000, week 2)Back to main SAS-L pageJoin or leave SAS-L (or change settings)ReplyPost a new messageSearchProportional fontNon-proportional font
Date:         Thu, 10 Aug 2000 14:03:42 -0700
Reply-To:     Laurie Fleming <lflemingNOlfSPAM@ACTRIX.GEN.NZ.INVALID>
Sender:       "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From:         Laurie Fleming <lflemingNOlfSPAM@ACTRIX.GEN.NZ.INVALID>
Organization: http://www.remarq.com: The World's Usenet/Discussions Start Here
Subject:      Re: Number of observations in a table of a Oracle Library

Here's my macro (with accompanying error display macro putmsg) which will dynamically generate the oracle (or sybase) query to find the number of observations.

To use: Create a view of the Oracle table. (proc sql; create view oracle.table_view as select *

%countobs(dsn=oracle.table_view, oracle=y) %put &_nobs;

Macro variable _nobs contains the number of rows in the table.

If you omit the oracle parameter, the macro does a SAS count of the view - hugely inefficient, but it does work.

The weight parameter multiplies the number of observations by the value in the variable/column.

Obviously this works on any SAS data object - SAS view, dataset, xport - as well, but is here optimised for Sybase and Oracle (which have subtle differences in their syntax).

Any where clause stuff you put in will be passed directly to the Oracle query.

%macro countobs(dsn=, sybase=n, oracle=n, where=, weight=, debug=n) / des='Get number of obs in a dataset or view'; /* Macro name: countobs Source: countobs.sas under ~flemingl Author: Laurie Fleming, Sysware Consulting Group Ltd (laurie@sysware.co.nz) Date written: 12 February 1997 Purpose: Count observations in a SAS dataset or view.

Parameters: dsn - dataset name. sybase - Generate sybase query to count rows oracle - Generate oracle query to count rows where - Where clause weight - Weight the result by the column/variable name

Macros required: putmsg - display error messages

Method: %global/%local/%let Define variables and initialise them. %if &dsn... Parse parameters %if &sybase = Y and %eval... If it's a Sybase table and the dsn is a view, get the connection information from the view description. From this, generate an sql connection and get Sybase to count the number of rows. This could be a security issue, if the table is owned by somebody else. %if %sysfunc(exist... Check existence and member type. attrn(&dsn, nlobs) Get the number of logical observations, if available. This is the total observations minus the deleted observations. If not available, data _null_ physically read and count observations. %EndMac That's the end...

Modification history (reverse chronological order) Laurie Fleming, 12 April 2000 Added Oracle funtionality.

Laurie Fleming, 26 April 1999 Allowed for sequential-engined XPORT datasets to be counted.

Laurie Fleming, 11 March 1998 Changed counter from 'count' to '_count' - this reduces the chances of undefined results because of the source data having a variable used by the 'data _null_'.

Laurie Fleming, 24 August 1997 Allowed 'where' processing to be done on a Sybase view.

Laurie Fleming, 13 August 1997 Added new parameter 'where', to subset the dataset.

Laurie Fleming, 4 July 1997 Changed name of global variable __error to __cnterr, to avoid conflicting with __error in sybload.

Laurie Fleming, 9 May 1997 Changed reset of 'proc printto' log to standard log. When SAS runs in background this doesn't work very well otherwise (with just 'proc printto'). Corrected bug in scanning of Sybase view where the 'from ' and Sybase table name are split over two lines. SAS was trying to read past the end of the variable.

2 May 1997 Included Sybase row counting - faster than SAS's counting of rows in a Sybase view by several orders of magnitude.

April 1997 First written. */

/* Declare global and local variables. */ %global _nobs _dbase _table __cnterr; %let __cnterr = 1; run; quit; %local notes mprint symblgen mlogic ls source2 fn saverc memtype engine libname dbms;

/* Get option information. */ %let mprint = %sysfunc(getoption(mprint)); %let notes = %sysfunc(getoption(notes)); %let symblgen = %sysfunc(getoption(symbolgen)); %let mlogic = %sysfunc(getoption(mlogic)); %let source2 = %sysfunc(getoption(source2)); %let ls = %sysfunc(getoption(linesize));

/* Parse parameters. */ %if &dsn = %then %do; %putmsg(msg=DSN is a compulsory parameter.); %let _nobs = ERROR; %goto EndMac; %end;

%if &sybase = %then %let sybase = N; %else %let sybase = %upcase(%substr(&sybase, 1, 1));

%if &oracle = %then %let oracle = N; %else %let oracle = %upcase(%substr(&oracle, 1, 1));

%if &debug = %then %let debug = N; %else %let debug = %upcase(%substr(&debug, 1, 1));

%if &debug = N %then %str(option nomprint nonotes nosymbolgen nomlogic nosource2;); %else %str(option mprint symbolgen mlogic notes source2;);

%let where = %quote(%unquote(&where)); %let weight = %quote(%unquote(&weight));

%if %eval(%index(&dsn, .) = 0) %then %let libname = work; %else %do; %let libname = %scan(&dsn, 1, .); %let dsn = %scan(&dsn, 2, .); %end;

/* Initialise number of observations, in case no number can be found. */ %let _nobs = .;

/* Check if dataset or view exists, and what memtype (data or view) it is. */ proc sql noprint; select memtype, engine into :memtype, :engine from dictionary.members where libname = "%upcase(&libname)" and memname = "%upcase (&dsn)"; quit; %if &memtype = %then %do; %putmsg(msg=Dataset &libname..&dsn does not exist.); %let _nobs = ERROR; %goto EndMac; %end;

%if (&sybase = Y or &oracle = Y) and &memtype = VIEW %then %do; %if &sybase = Y %then %let dbms = sybase; %else %let dbms = oracle; /* Set up file references to contain log from view description, and code generated from examining the description. */ filename descview catalog 'work.countobs.descview.log'; filename gencount catalog 'work.countobs.gencount.source'; /* Change linesize to 200, to make scanning of log more reliable (it works better, and faster, with long record lengths). */ option ls=200; /* Move log to descview (see above), instead of the log window. */ proc printto log=descview new;

/* Put description of view into the log. Part of what it contains is the connection information, including Sybase user id and password (!). This could be a security issue. */ proc sql; describe view &libname..&dsn; quit;

/* Reset the log and put the linesize back to what it was. */ proc printto log=log; run;

option ls=&ls;

/* Read the log and from it pick out the salient data, reformatting it and generating the code to count the number of rows in the Sybase/Oracle table referenced by the SAS view. */ data _null_; infile descview flowover n=2 column=colnum line=linenum ls=200 length=linelen; file gencount; retain m1 -1 spltline 0; length connect record $ 200 database tablenme $ 30; /* Read through the log until the string 'options=' is found. Note the use of '#(linenum - 1)'. I don't know why this is required (it isn't later when much the same input style is used), but this is the only way I could get it to work. Oddly the input, despite the trailing '@', still goes onto the next line and it's necessary to go backwards a line (with a bit of help from the 'n=2' on the infile). */ input @'options=' @; index1 = colnum; /* Save the column position. */ input #(linenum - 1) @1 connect $varying200. linelen; index2 = indexc(connect, ')') - index1 + 1; /* Get the length of the connection string. */ %if &sybase = Y %then %do; database = substr(connect, index(connect, 'database="') + 10, 30); call symput('_dbase', scan(database, 1, '"')); %end; connect = substr(connect, index1, index2) || ';'; /* Start generating the SAS SQL code to count the rows. */ put 'proc sql noprint;' / "connect to &dbms" connect / 'select count' / ' into :_nobs' / " from connection to &dbms"; /* Scan further through the log to find the string 'from '; the word after this string is the name of the Sybase table. Save the column position, because it will be overwritten with the next input. */ input @'from ' @; index1 = colnum; /* It's possible for the 'from' and the Sybase table name to be split over two lines. If they are, the column position that the pointer is set at is greater than the line length, so this has to be flagged and the current line to be finished with. */ if index1 ge linelen then do; spltline = 1; input; end; input @1 record $varying200. linelen; if spltline then /* Find the first non- space, non-parenthetical character. */ index1 = verify(record, ' )('); /* Pick up the tablename from the record. */ tablenme = scan(substr(record, index1), 1, ' )('); call symput('_table', trim(tablenme)); %if &weight = %then %do; %if &sybase = Y %then %do; put @4 "(select count(*) as 'count' " %end; %else %do; put @4 "(select count(*) as count " %end; %end; %else %do; %if &sybase = Y %then %do; put @4 "(select sum(&weight) as 'count' " %end; %else %do; put @4 "(select sum(&weight) as count " %end; %end; / @7 'from ' tablenme +m1 @; %if &where ne %then %do; put / @6 "where &where" @; %end; put ');' / "disconnect from &dbms;" / 'quit;'; stop; run;

/* Now that the code has been generated and stored in the catalog(ue), include (execute) it. The result (count) is placed in the global macro variable _nobs. */ %inc gencount; %let saverc = &sqlrc;

/* Purge the catalog(ue) which contains the log and source. */ filename descview clear; filename gencount clear;

%if &debug = N %then %do; proc datasets lib=work nolist nowarn mt=catalog; delete countobs; quit; %end; /* Drop out... */ %goto Success; %end;

/* If it's a dataset, get number of total and deleted observations, if available. */ %if &engine ne XPORT and &memtype = DATA %then %do; %let fn = %sysfunc(open(&libname..&dsn, i)); /* Create an open tag to the dataset/view */ %if %eval(&fn = 0) %then %do; %putmsg(msg=Cannot open dataset/view &libname..&dsn..); %let _nobs = ERROR; %goto EndMac; %end;

%let _nobs = %sysfunc(attrn(&fn, nlobs)); /* Get the number of logical observations */ %let fn = %sysfunc(close(&fn)); /* Close the dataset/view */ %end;

/* If the result is '-1' the number of logical observations cannot be ascertained, so a data step (as opposed to an SQL query - that'd be slightly less efficient) is generated to count the rows. */ %if &engine = XPORT or &memtype = VIEW or %eval(&_nobs = -1) or &_nobs = . or &where ne or &weight ne %then %do; %if &weight = %then %let weight = 1; %let _nobs = 0; data _null_; set &libname..&dsn %if &where = and &weight = 1 %then %do; (drop=_all_) %end; end=eof; %if &where ne %then %do; where &where; %end; retain _count 0; _count + &weight; if eof then call symput('_nobs', left(put(_count, best15.))); run; %end; %Success: %let __cnterr = 0;

%EndMac: option &source2 &notes &symblgen &mlogic &mprint; %mend countobs;

%macro putmsg(msg=, type=ERROR) / des='Display macro error messages in the log'; %if &type = %then %let type = ERROR; %else %let type = %upcase(&type); %put; %let msg = %quote(&msg); %if &msg = %then %put &type: (No further information); %else %put &type: &msg; %mend putmsg;

-----------------------------------------------------------

Got questions? Get answers over the phone at Keen.com. Up to 100 minutes free! http://www.keen.com


Back to: Top of message | Previous page | Main SAS-L page