2017-08-01 2 views
1

Ich versuche automatisch einige Integritätsbedingungen auf diesem Datensatz basierten zu erstellen:SAS: Automatisieren Integritätsbedingungen Schaffung

ds_name | var_name | ic_clause       | ic_msg 
--------+----------+------------------------------------+----------------------- 
tableA | var1  | primary key($var$)     | $var$ is a primary key 
tableB | var2  | check(where=($var$ in ('a', 'b'))) | invalid $var$ value 

Die Idee ist, ein generisches Programm zu erstellen, die durch diese Datensatz Schleife und erstellt die ICs entsprechend. In diesem speziellen Fall ist das äquivalent hart codierte Programm wäre:

proc datasets nolist; 
    modify tableA; 
     ic create primary key(var1) 
      message = "var1 is a primary key"; 
quit; 

proc datasets nolist; 
    modify tableB; 
     ic create check(where=(var2 in ('a', 'b'))) 
      message = "invalid var2 value"; 
quit; 

Dies sind die Schritte, die ich im Programm vorstellen, aber ich brauche Hilfe, um sie in der eigentlichen Code zu übersetzen:

  1. Fetch Werte für eine Zeile und setzen sie sie in Makrovariablen
  2. ersetzen $ var $ Substrings mit den tatsächlichen Variablennamen in Spalte var_name
  3. Führen Sie einen generischen proc Datensätze, zB .:

    proc datasets nolist; 
         modify &my_ds; 
          ic create &my_clause 
           message = &my_msg; 
        quit; 
    
  4. Schleife durch alle Reihen

Kann jemand mir bitte mit diesem Code helfen? Ich weiß nicht, ob die von mir vorgeschlagenen Schritte der beste Weg sind, um das zu implementieren, was ich versuche. Im Grunde versuche ich eine relationale Datenbank innerhalb von SAS zu simulieren und Dinge maximal zu automatisieren.

Vielen Dank!

+0

http://www2.sas.com/proceedings/sugi25/25/cc/25p077.pdf – user667489

+0

@ user667489 Dank für den Artikel! Datengesteuerte Programmierung ist wirklich ein interessantes Konzept. –

Antwort

0

Sie werden wahrscheinlich feststellen, dass Sie SAS nicht in ein DBMS verwandeln können. Es ist möglicherweise besser, Ihre Metadaten zu verwenden, um Programme zu generieren, die die Daten überprüfen, anstatt zu versuchen, Integritätsbedingungen zu implementieren.

Aber das Konzept der datengetriebenen Code-Generierung ist interessant, also sehen wir, ob wir anhand Ihres Beispiels demonstrieren können, wie man Code aus Metadaten generiert. Ich finde, dass es besser funktioniert, wenn Sie die Variablennamen in den Metadaten mit dem Code vergleichen, der generiert werden soll. So können wir die Variable aufrufen, die verwendet wird, um die MESSAGE= Option auf der IC-Anweisung MESSAGE zu erstellen.

Jetzt können wir einen einfachen Datenschritt verwenden, um den Code zu generieren.Nicht sicher, warum Sie Pseudocode in den Constraint- und Nachrichtenfeldern verwendet haben, anstatt nur die Werte hart zu codieren, aber wir können TRANWRD() Funktion verwenden, um die $varname$ Zeichenfolgen durch den Wert der VARNAME Variable zu ersetzen.

Lassen Sie uns eine Beispielmetadatendatei erstellen.

data ic_metadata; 
    infile datalines dlm="|"; 
    length libname $8 memname $32 varname $32 constraint message $200; 
    input libname memname varname constraint message ; 
datalines; 
work|tableA|var1|primary key($varname$)    |$varname$ is a primary key 
work|tableB|var2|check(where=($varname$ in ('a', 'b')))|invalid $varname$ value 
; 

Und einige Beispieldaten zu arbeiten.

data tablea tableb ; 
length var1 8 var2 $8 ; 
var1+1; 
var2='a'; 
run; 

Lassen Sie uns jetzt die Metadaten verwenden, um den Code zu generieren und %INCLUDE um es auszuführen.

filename code temp; 
data _null_; 
    file code ; 
    set ic_metadata ; 
    by libname memname ; 
    if first.libname then put 'proc datasets lib=' libname 'nolist;' ; 
    if first.memname then put ' modify ' memname ';' ; 
    constraint=tranwrd(constraint,'$varname$',trim(varname)); 
    message=tranwrd(message,'$varname$',trim(varname)); 
    put 'ic create ' constraint message= :$quote. ';' ; 
    if last.memname then put 'run;'; 
    if last.libname then put 'quit;' ; 
run; 
%include code/source2 ; 

So läuft das Beispiel wir ein SAS-Log wie diese:

161 +proc datasets lib=work nolist; 
162 + modify tableA ; 
163 +ic create primary key(var1) message="var1 is a primary key" ; 
NOTE: Integrity constraint _PK0001_ defined. 
164 +run; 

NOTE: MODIFY was successful for WORK.TABLEA.DATA. 
165 + modify tableB ; 
166 +ic create check(where=(var2 in ('a', 'b'))) message="invalid var2 value" ; 
NOTE: Integrity constraint _CK0001_ defined. 
167 +run; 

NOTE: MODIFY was successful for WORK.TABLEB.DATA. 
168 +quit; 
+0

Was sind die Vorteile bei der Verwendung Ihres Ansatzes, anstatt Call Execute? P.S. In der Tat, vielleicht sollte ich den "SAS-Weg" machen und aufhören zu versuchen, DBMS-Funktionen zu replizieren. Ty für den Rat! –

+0

Das Schreiben einer physischen Datei anstelle von CALL EXECUTE ist einfacher zu debuggen, da Sie die Datei überprüfen und die Syntax überprüfen können, bevor Sie sie ausführen. Sie können den Code so formatieren, dass das Protokoll übersichtlicher ist. Außerdem können Sie Funktionen der Anweisung PUT nutzen, wie im Beispiel die Nachricht 'message =: $ quote'. Beim Generieren von Makroaufrufen werden auch die Zeitprobleme vermieden, die auftreten können, wenn Makroaufrufe über CALL EXECUTE gesendet werden. – Tom

0

Sie können die Datenvariablen selbst verwenden, um Ihre Makroanweisungen wie von Ihnen vorgeschlagen zu schreiben. Im Grunde möchten Sie eine neue, lange Variable erstellen, so wie ein Makroaufruf in einer Datenkette für jede Zeile aussehen würde, indem Sie alle Variablen zusammenfassen. Sie können die tranwrd Funktion verwenden, um den Platzhaltertext durch den tatsächlichen VAR_NAME zu ersetzen. Im Folgenden soll funktionieren:

data test; 
    infile datalines dlm="|"; 
    length DS_NAME VAR_NAME IC_CLAUSE IC_MSG $50; 
    input DS_NAME $ VAR_NAME $ IC_CLAUSE $ IC_MSG $; 
    datalines; 
    tableA | var1  | primary key($var$)     | $var$ is a primary key 
    tableB | var2  | check(where=($var$ in ('a', 'b'))) | invalid $var$ value 
    ; 
run; 

** write your master macro **; 
%MACRO master_loop(DS_NAME=,IC_CLAUSE=,IC_MSG=); 
proc datasets nolist; 
    modify &DS_NAME.; 
     ic create &IC_CLAUSE. 
      message = "&IC_MSG."; 
quit; 
%MEND; 

** create all your macro statements **; 
data master_strings; 
    length STR $200; 
    set test; 
    IC_CLAUSE1 = tranwrd(IC_CLAUSE,"$var$",strip(VAR_NAME)); /* replace the placeholder with the actual VAR_NAME contents */ 
    IC_MSG1 = tranwrd(IC_MSG,"$var$",strip(VAR_NAME)); /* replace the placeholder with the actual VAR_NAME contents */ 
    STR = %nrstr("%master_loop("||"DS_NAME="||strip(DS_NAME)||",IC_CLAUSE="||strip(IC_CLAUSE1)||",IC_MSG="||strip(IC_MSG1)||");"); 
run; 

** put all macro statements into a list**; 
** this would look similar to writing out multiple %master_loop statements if hard-coded **; 
proc sql noprint; 
    select STR 
    into: all_macro_calls separated by " " 
    from master_strings; 
quit; 

** submit all your macro calls **; 
%put &all_macro_calls.; 
0

Sie call execute können Ihr "einprogrammierten Programm vollständig dynamisch (wobei IC Ihre Basis-Datensatz mit den Einschränkungen) zu machen:

data _null_; 
set IC; 
call execute("proc datasets nolist;modify "||strip(ds_name) 
    ||";ic create "||tranwrd(strip(ic_clause),'$var$',strip(var_name)) 
    ||" message = '"||tranwrd(strip(ic_msg),'$var$',strip(var_name)) 
    ||"';quit;"); 
run; 

Grundsätzlich für jede Beobachtung in Ihrem Datensatz call execute führt den entsprechenden proc datasets aus, indem er die Variablenwerte (ds_name, var_name usw.) an der richtigen Stelle einfügt. Die tranwrd Funktionen werden dafür sorgen, den $var$ Platzhalter durch den tatsächlichen Wert var_name zu ersetzen.

+0

Vielen Dank! Es ist sicherlich die meisten KISS Antwort (halten Sie es einfach, dumm). Allerdings habe ich Toms Antwort gewählt (die Ihrer ähnlich ist), da sie einige Debugging-Vorteile bietet. –

+0

@WillianRazente keine harten Gefühle;) – user2877959

Verwandte Themen