2017-08-01 123 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