Aufteilung (und späteres separates Einlesen) einer großen Access-Tabelle anhand eines Kriteriums
Liebe Redscope-Community,
ich greife mit SAS auf eine sehr große Access-Tabelle zu, die ich aus Kapazitätsgründen aufteilen muss und habe eine Frage zur Umsetzung des entsprechenden Algorithmus:
Die Tabelle besteht aus knapp 1Mio Zeilen (Zeitreihe mit verschiedenen Fonds) und ca 50 Spalten, die wie in der folgenden schematischen Übersicht dargestellt, aufgebaut ist:
ID - datum - Wert1 - Wert2 - usw.
13 - 1.1.01 - Werte
13 - 1.2.01 - Werte
13 - 1.3.01 - Werte
...
13 - 1.1.06 - Werte
16 - 1.1.01 - Werte
16 - 1.2.01 - Werte
usw.
Nun will ich diese große Tabelle anhand der ID in mehrere kleine aufteilen (d.h. alle Zeilen mit gleicher ID sollen in jeweils eine neue Tabelle mit der Bezeichnung ID gepackt werden). Dabei können die Zeitreihen je ID unterschiedliche Längen aufweisen.
Hat jemand eine Ahnung, wie ich das am effizientesten bewerkstelligen kann? Die IDs sind übrigens nicht zwingend forlaufend.
In einem späteren Schritt soll dann für alle (aus den verschiedenen IDs erstellten) Tabellen gewisse Rechenoperationen durchgeführt werden ("Führe mir für jede Tabelle folgende Berechnung aus"), wobei die Tabellennamen/-nummern wiederum nicht aufeinander folgend sein müssen.
Gibt es auch hierfür eine Möglichkeit über eine Art Schleife jede Tabelle in einem gewissen Library einzulesen?
Vielen Dank schon mal für Eure Hilfe!
- Anmelden oder Registrieren um Kommentare zu schreiben

Aufteilen
anbei ein schematisches Beispiel nur anhand der Variablen ID.
Hier werden die verschiedenen ID's aus der Tabelle grosseTabelle mittels PROC SQL bestimmt und danach in einer Macroschleife in Macrovariablen, die dann fortlaufend nummeriert sind, eingelesen.
Danach wird die grosseTabelle mit einem WHERE-Statement eingelesen und in kleiner Tabellen aufgeteilt, die entsprechend der ID benannt sind. In diesem und den folgenden Datasteps/Proc-Steps können dann beliebige Berechnungen für diese ID durchgeführt werden.
input ID $3.;
cards;
001
003
005
006
006
006
008
008
;
run;
%macro aufteilung(grosseTabelle);
proc sql;
create table diffID as select distinct(ID)
from &grosseTabelle;
quit;
%let dsid=%sysfunc(open(diffID));
%let nobs=%sysfunc(attrn(&dsid,nobs));
%put &nobs;
%do i = 1 % to &nobs;
%let x=%sysfunc(fetchobs(&dsid,&i));
%let id_&i.=%sysfunc(getvarc(&dsid,1));
%put Berechnungen für ID=&&id_&i.;
data ID_&&id_&i.;
set &grosseTabelle.(where=(id="&&id_&i."));
/* beliebige Berechnungen */
run;
data Berechnung1_ID_&&id_&i.;
set ID_&&id_&i.; /* Einlesen der nun kleineren Tabelle */
nummer=input(id,3.)-_n_;
run;
proc sort data=Berechnung1_ID_&&id_&i.;
by nummer;
run;
/* weitere beliebige Berechnungen */
%end;
%let rc=%sysfunc(close(&dsid));
%mend;
%aufteilung(grosseTabelle);
Ich hoffe es hilft.
Gruß
Wolfgang
Vielen Dank für die
Vielen Dank für die unglaublich schnelle Antwort ;-)
Eine Anmerkung habe ich noch:
Da erst in einem wesentlich späteren Programmteil die Berechnung(en), die für alle neu erzeugten ID-Tabellen durchlaufen werden sollen, durchführe:
Gibt es eine Möglichkeit, die Berechnungen ausserhalb der Schleife zu positionieren und dann sukzessive alle Tabellen eines Verzeichnisses aufrufen und jeweils dafür die Berechnungen abzuarbeiten?
alle Tabellen eines Verzeichnisses aufrufen
Es gibt viele Möglichkeiten dazu, aber alle sind "zu Fuß", also mehr oder weniger elegant (und mit Macro-Code) erstellt. Eine Version könnte so aussehen:
proc sql;
select memname
into:NameList
from sashelp.vtable
where upcase(libname) = "%upcase(&_lib.)"
AND memname LIKE "%upcase(&_name.)%nrbquote(%)"
;
run; quit;
%do ii = 1 % to 999999;
%if %scan(&NameList.,&_ii.) EQ
%then %do;
%*put INFO: Schleifen-Ende ;
%let ii=%eval(&ii. + 999999); /* Schleife-Abbruch */
%end
%else %do;
%let dsname=%scan(&NameList., &ii.);
data Ber1_&dsname.; /* neuer Name passend zu altem */
set &dsname.;
Berechnung= bla bla bla;
run;
%end
%end; /* end_of_DO-Schleife */
%mend Berechnungen;
/* alle Data Sets im work die mit ID_ anfangen bearbeiten: */
%Berechnungen(work, ID_);
Wie gesagt, das ist eine Möglichkeit ...
Gruß
Hans Kneilmann, Schäfer Shop GmbH (SSI)
Vielen Dank für die schnell
Vielen Dank für die schnell Antwort!
Ich habe übrigens nach einiger Recherche drei Codebeispiele für die Aufteilung gefunden, die ich der Einfachheit halber auch noch poste (Quellenangabe unten):
Solution #1 by
Diskin, Dennis
Email: Dennis.Diskin@PHARMA.COM
Will work as long as the variable to be used is NUMERIC (Waferno).
PROC SQL NOPRINT;
SELECT distinct Waferno into :w1 thru :w9999 FROM test;
%let nw = &sqlobs;
DATA %do i = 1 %to &nw;
out&&w&i %end; ;
set test;
select (Waferno);
%do i = 1 %to &nw;
when (&&w&i) output out&&w&i;
%end; end;
%mend;
%wafer;
run;
Solution #2 by
Rob Workman
Email: Rob.Workman@SULZERCARBOMEDICS.COM
Will work as long as the variable to be used is NUMERIC (Waferno). But
can be modified easily to work for a Character Variable.
by waferno;
run;
data new;
set test ;
by class ;
if first.waferno then do;
call execute ('data w'||compress(put(waferno, best.))||';');
call execute ('set stuff(where=(waferno = ||compress(put(waferno,best.))||'));');
call execute ('run;');
end;
run;
Solution #3 by
Charles Patridge
Email: Charles_S_Patridge@prodigy.net
Solution works for both Numeric and Character but involves the use of
3 macros (VARTYPE, EMPTYYN and SPLITTER). The Values of Variable to
use to break dataset into smaller pieces needs to be less 8 characters
in length for SAS version 6.
/*** Not as efficient as Dennis' or Rob's ***/
/*** but does work for either numeric or character data ***/
/*** provided the length is le 7 (version 6 limit) ***/
/*****************************************************************/
/***VARTYPE.SAS ***/
/*** Determine what type of variable a SAS variable is ***/
/*** N = Numeric or C = Character ***/
/*** MACRO ***/
/*** SASDSN = Name of SAS Dataset ***/
/*** SASVAR = Name of SAS Variable ***/
/*** ***/
/*** Macro Variable VARTYPE holds the Variable Type ***/
/*****************************************************************/
%global vartype ;
%macro VARTYPE ( SASDSN, SASVAR );
%let dsid = %sysfunc( open ( &SASDSN,i) );
%let varnum = %sysfunc( varnum ( &dsid, &sasvar ) );
%let vartype = %sysfunc( vartype(&dsid, &varnum) );
%let rc = %sysfunc( close ( &dsid ) );
%mend VARTYPE;
*** Sample Call;
***%vartype( cpmsas.cpm10x, ml );
/*** end of sas program - vartype ***/
*********************************************************************;
** PROGRAM: EMPTYYN (MACRO) ;
** AUTHOR: CHUCK PATRIDGE ;
** DATE: 10/12/94 ;
** PURPOSE: DETERMINE IF A SAS DATASET IS EMPTY. ;
** INPUT PARAMETER: DSNAME (NAME OF SAS DATA SET) ;
** ALSO CAN BE BLANK ( USE LAST DATASET CREATED) ;
** OUTPUT: WILL CREATE THE FOLLOWING GLOBAL MACRO VARIABLES ;
** EMPPTYYN Y=EMPTY, N=NONEMPTY ;
** NUMOBS WHICH PROVIDES NUMBER OF RECORDS ;
** DSN WHICH PROVIDES NAME OF DATA SET ;
** SAMPLE CALL: %EMPTYYN(DATASET) ;
** ;
*********************************************************************;
%GLOBAL EMPTYYN NUMOBS DSN;
%MACRO EMPTYYN(DSNAME);
DATA _NULL_;
IF "&DSNAME " = " " THEN CALL SYMPUT('DSNAME','_LAST_');
RUN;
DATA _NULL_;
IF 0 THEN SET &DSNAME NOBS=NUMOBS;
IF NUMOBS > 0 THEN EMPTYYN = 'N';
ELSE EMPTYYN = 'Y';
CALL SYMPUT('EMPTYYN',PUT(EMPTYYN, $1.));
CALL SYMPUT('NUMOBS' ,PUT(NUMOBS , BEST.));
CALL SYMPUT("DSN" ,PUT("&DSNAME" , $VARYING17.));
STOP;
RUN;
%MEND EMPTYYN;
/*** END OF SAS PROGRAM - emptyyn ***/
/******************************************************************/ /*SPLITTER.SAS SPLITTER MACRO */ /* READ FILE IN and BREAK UP FILE INTO SMALLER FILES BASED UPON */ /* UNIQUE VALUES OF A VARIABLE */ /* */ /* 1st Parm = SASLIB - SAS Library */ /* 2nd Parm = SASDSN - SAS Dataset Name */ /* 3rd Parm = SASVAR - SAS Variable to use for Break Up Values */ /* 4th Parm = PREFIX - Prefix of OUTPUT SAS Dataset Names */ /* */ /* Written for version 6 SAS Datasets so names LE 8 characters. */ /* */ /******************************************************************/ %MACRO SPLITTER( SASLIB, SASDSN, SASVAR, PREFIX );
%global vartype;
PROC SORT DATA=&SASLIB..&SASDSN(KEEP=&SASVAR) OUT=WORK.&SASDSN NODUPKEY;
BY &SASVAR;
RUN;
%EMPTYYN(WORK.&SASDSN);
%vartype(work.&SASDSN, &SASVAR);;;
%put &vartype ;
%MACRO LOOPDIRS(HOWMANY);
%DO KK = 1 %TO &HOWMANY;
DATA _NULL_;
RECD = &KK;
SET WORK.&SASDSN POINT=RECD;
length _breakv $ 7 ;
%if "&vartype" = "N" %then %do;
_breakv = Trim(left(put( &SASVAR , best.)));
%end;
%if "&vartype" = "C" %then %do; _
breakv = Trim( left( &SASVAR ) );
%end;
CALL SYMPUT('_breakv' , TRIM(_breakv) );
STOP;
RUN;
DATA &PREFIX.&_BREAKV;
SET &SASLIB..&SASDSN;
%if &vartype = N %then where &SASVAR = &_BREAKV ;;;
%if &vartype = C %then where &SASVAR = "&_BREAKV ";;;
RUN;
%END;
%MEND LOOPDIRS;
%LOOPDIRS( &NUMOBS );
%MEND SPLITTER;
/*** SAMPLE CALL IS ***/ ***%SPLITTER( work, test, Class , CLS );
%SPLITTER( work, test, Waferno, WFN );
Quelle:
http://www.sconsig.com/sastips/tip00302.htm