Dynamisch SAS-Code erzeugen und Ausführen mit Call Execute ()

Aus SAS-Wiki
Wechseln zu: Navigation, Suche

Von Wilfried Schollenberger aus: Hans-Peter Altenburg, Carina Ortseifen, Tanja Petrowitsch, Grischa Pfister, Wilfried Schollenberger: Tipps & Tricks für den leichteren Umgang mit der SAS Software, KSFE 2007

Einführung

Normalerweise wird dynamisch erzeugter SAS-Code in eine temporäre Datei geschrieben und dann mit %Include eingelesen. Beispiel:

	Filename programm TEMP;
	Data _null_;
  		File programm;
  		Put '%put Das bin ich;';
	Run;
	%include programm;

Dagegen ist normalerweise nichts einzuwenden, solange man die Umgebung, in der der Programmteil läuft, kennt. Anders ist das bei allgemein verwendeten Programm-Teilen und Autocall-Makros. Z.B. könnte der Benutzer vorher den Filename schon vergeben haben:

	Filename programm '~/sas/meineProgramme';

Nachdem unser Programm-Teil ausgeführt wurde, wird er seine Programme nicht mehr finden, es sei denn, er setzt seine Filename-Anweisung erneut ab. Dieses Problem lässt sich vermeiden, wenn auf die temporäre Datei verzichtet und der SAS-Code mit der Routine Call Execute() übergeben wird. Obiges Beispiel sähe dann so aus:

	Data _null_;
  		call execute('%Put Das bin ich;');
	Run;

Beispiel 1: Bedingte Erstellung von Auswertungen

Zum Beispiel könnte man in einer SAS-Datei eine Liste von Auswertungs-Programmen speichern, und mit einem Kennzeichen steuern, ob die jeweilige Auswertung in einem Nacht-Lauf tatsächlich ausgeführt werden soll( Da „freut“ sich dann der Operator, wenn er nicht weiß, wie lang der Batch-Job laufen wird, und deshalb nicht richtig planen kann. Andererseits ist es natürlich sinnvoll, aufwendige Auswertungen möglichst in die Nacht-Verarbeitung zu legen und nur bei Bedarf auszuführen) .

	Data _NULL_;
   		Set ksfe.auswertungen;
               if starten GT " " then
      		call execute(auswertung);
	Run;

Oder man macht die Auswertung davon abhängig, ob eine Datei geändert wurde:

	Data ksfe.reports;
   		Set ksfe.reports;

   		dsid = open(datei,“I“);

   		if attrn(dsid,“modte“)) GT letzter_Stand then do;
      		call execute(auswertung);
      		letzter_Stand = attrn(dsid,“modte“));
   		end;

   		dsid = close(dsid);
	Run;

In der Variablen Auswertung wird vermutlich ein Makro-Aufruf stehen. Man kann mit Call Execute() aber auch normalen SAS-Code absetzen, wie in dem folgenden Beispiel.

Beispiel 2: Liste von Datenbank-Tabellen mit der Anzahl der Sätze

Hintergrund ist, dass die Dictionary-Tabellen von SAS bei Views und Datenbanken die Anzahl der Beobachtungen nicht enthalten. Wenn man nun eine Übersicht über alle Tabellen und ihre Größe benötigt, kann man diese mit dem folgenden Programm erzeugen:

	/** Verzeichnis der Tabellen erstellen */
	Proc sql; 
   		Create table work.dir as
      		select trim(libname) !! "." !! memname as dsname,  
				  memname
      		from   dictionary.tables                             
      		where libname EQ "DB2LIB"
      		order by memname;                                     
	Quit;
	
	/** Anzahl der Records in jeder Tabelle ermitteln */
	Data _null_;                                                    
 		Set work.dir end=finale;                                       

 		if _n_ EQ 1 then 
			call execute('Proc Sql;'
				!!' create table work.dir_nobs as ');      
  		else call execute(' outer union corresponding ');          
                                                   
 		call execute(' select "' !!memname
				!! '" as Name,'
				!! ' count(*) as records Format=commax12.0'          
                	!! ' from ' !! dsname); 
                               
	 	if finale;                                             
 		call execute(';Quit;');                                
	Run;
	/** Liste ausgeben */
	Proc print data=work.dir_nobs label;
		Var memname records;
		Label memname = "Tabelle"
               records = "Anzahl Sätze"
		;
	Run;

Besonderheit bei Makros

Zu beachten ist, dass Makros sofort, noch während des Laufs des Daten-Schritts ausgeführt werden. In dem folgenden Programm wird dieser Effekt demonstriert. Das Makro zeigt immer den zur Laufzeit aktuellen Inhalt der Makro-Variablen 'Name':

	%macro zeigeName;
		%put &name;
	%mend;
	Data _null_;
    		Set ksfe.bsp1;
    		call symput('Name', name);
    		call execute('%zeigeName; ');
	Run; 

Wenn man das vermeiden will, muss die Makro-Funktion %Nrstr() verwendet werden:

	Data _null_;
    		Set ksfe.bsp1;
    		call symput('Name',name);
    		call execute('%nrstr(%zeigeName); ');
	Run;

Jetzt wird immer der zuletzt in die Makro-Variable gestellte Wert ausgegeben. Das ist sehr wichtig, wenn in einem Makro SAS-Anweisungen und Makro-Verarbeitung verknüpft werden. Z.B. funktioniert das folgende Makro nur in Verbindung mit %Nrstr():

	%macro testefile(n);
   		Filename ao "&n";
   		%if %sysfunc(fexist(ao)) 
			%then %put Die Datei &n existiert;
              	%else %put Die Datei &n existiert nicht;
	%mend;

Die Filename-Anweisung kann erst nach dem Daten-Schritt ausgeführt werden. Also darf auch der Aufruf der Funktion Fexist() erst nach dem Datenschritt ausgeführt werden.

Literatur

  • SAS Language Guide, ab Version 8, Beschreibung von call execute()