2017-08-01 70 views
1

我試圖自動創建基於此數據集某些完整性約束:SAS:自動化完整性約束創建

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 

的想法是創建一個通用的程序,通過這個數據集循環並創建相應的集成電路。在這種特殊情況下,等效硬編碼的程序應該是:

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; 

這些都是我在節目想象中的步驟,但我需要幫助,將其轉化爲實際的代碼:

  1. 獲取的值一排,放在宏觀變量
  2. 實際變量名稱替換是$ var $子在列VAR_NAME
  3. 通過所有行

任何人都可以請幫我這個代碼運行的通用PROC數據集,例如:

proc datasets nolist; 
     modify &my_ds; 
      ic create &my_clause 
       message = &my_msg; 
    quit; 
  • 循環?我不知道我提出的步驟是否是實現我想要做的最好的方式。基本上我試圖模擬SAS中的關係數據庫,並儘可能自動化事物。

    謝謝!

  • +0

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

    +0

    @ user667489感謝爲文章!數據驅動編程確實是一個有趣的概念。 –

    回答

    0

    您可能會發現無法將SAS轉換爲DBMS。使用元數據生成檢查數據的程序可能會更好,而不是試圖實現完整性約束。

    但是數據驅動代碼生成的概念很有意思,所以讓我們看看我們是否可以使用您的示例演示如何從元數據生成代碼。當您將元數據中的變量名稱與需要生成的代碼進行匹配時,我發現它效果更好。因此,讓我們調用用於在IC語句MESSAGE上創建MESSAGE=選項的變量。

    現在我們可以使用一個簡單的數據步驟來生成代碼。不知道爲什麼在約束和消息字段中使用僞代碼而不是僅對硬編碼值進行編碼,但我們可以使用TRANWRD()函數將$varname$字符串替換爲VARNAME變量的值。

    所以我們來創建一個示例元數據文件。

    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 
    ; 
    

    還有一些示例數據需要處理。

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

    現在讓我們使用的元數據來生成代碼並%INCLUDE運行它。

    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 ; 
    

    所以運行我們得到了例如SAS日誌是這樣的:

    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

    使用你的方法而不是調用execute會有什麼好處?附:的確,也許我應該這樣做「SAS方式」並停止嘗試複製DBMS功能。 Ty的建議! –

    +0

    編寫物理文件而不是使用CALL EXECUTE更易於調試,因爲您可以在執行該文件之前查看該文件並檢查語法。您可以格式化代碼,使日誌更清晰。它還允許您利用PUT語句的特性,如示例中的'message =:$ quote.'。當生成宏調用時,它還避免了通過CALL EXECUTE提交宏調用時可能發生的時間問題。 – Tom

    0

    您可以使用數據變量本身來按照您的建議編寫宏語句。你基本上想要創建一個新的,長的變量,這個變量就像一個宏調用看起來像寫在每行的datastep中一樣,把所有的變量串起來。您可以使用tranwrd函數將佔位符文本替換爲實際的VAR_NAME。下面應該工作:

    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

    您可以使用call execute,讓您的「硬編碼」程序完全動態的(其中IC是你的基礎數據集的約束):

    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; 
    

    基本上,在每個觀察您的數據集call execute將通過在正確的位置插入變量值(ds_name,var_name等)來執行相應的proc datasetstranwrd函數將負責將$var$佔位符替換爲實際值var_name

    +0

    謝謝!這肯定是KISS的答案最多(保持簡單,愚蠢)。但是,我選擇了Tom的答案(與您的答案類似),因爲它具有一些調試優勢。 –

    +0

    @WillianRazente毫不費勁;) – user2877959