Recursion 递归地添加到SAS中的数据表中

Recursion 递归地添加到SAS中的数据表中,recursion,sas,Recursion,Sas,我是SAS的新手。我需要进行x迭代来填充名为MYRS的数据集 每次迭代都需要将表1与(表2+MYRS)减去MYRS表中已有的记录 然后,我需要用其他匹配项更新MYRS表。目标是跟踪一系列电子邮件 MYRS本质上是表1的副本,包含匹配的记录。有点棘手。(简化模式)表1可以有DUP 比如说 TABLE1: ID | EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4| 1 | A | s | d | F 2 | g | F | j | L 3 | z | x | L | v 4 |

我是SAS的新手。我需要进行x迭代来填充名为
MYRS
的数据集

每次迭代都需要
表1
与(
表2
+
MYRS
减去
MYRS
表中已有的记录

然后,我需要用其他匹配项更新
MYRS
表。目标是跟踪一系列电子邮件

MYRS
本质上是表1的副本,包含匹配的记录。有点棘手。(简化模式)<代码>表1可以有DUP

比如说

TABLE1:
ID | EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4|
1 | A | s | d | F
2 | g | F | j | L
3 | z | x | L | v
4 | z | x | L | v
2 | g | F | j | L

TABLE2:
EMAIL
A

MYRS (starts as empty dataset)
EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4
逻辑:
TABLE1
的电子邮件与
TABLE2
中的电子邮件相匹配。因此,需要显示此记录。其他记录与
表2中的任何内容都不匹配。但是由于
Record1
Record2
共享
相同的备选电子邮件
F
Record2
也需要显示。但是由于
Record2
Record3
共享相同的备选电子邮件L,因此也需要显示
Record3
。所以第四

proc sql;        
SELECT TABLE1.id,
    TABLE1.email1,
    TABLE1.email2,
    TABLE1.email3,
    TABLE1.email4
FROM TABLE1
INNER JOIN (
    SELECT EMAIL
    FROM TABLE2     
     UNION      
    SELECT EMAIL1 AS EMAIL
    FROM MYRS   
     UNION      
    SELECT EMAIL2 AS EMAIL
    FROM MYRS   
     UNION      
    SELECT EMAIL3 AS EMAIL
    FROM MYRS   
     UNION      
    SELECT EMAIL4 AS EMAIL
    FROM MYRS
    )
ON EMAIL=EMAIL1 OR EMAIL=EMAIL2 OR EMAIL=EMAIL3 OR EMAIL=EMAIL4
WHERE TABLE1.id NOT IN (
        SELECT DISTINCT ID
        FROM MYRS
        )
quit;
如何创建以下逻辑:

  • 把它包装成某种函数
  • 在执行sql之前,以MYDS计算记录的数量并保存该计数
  • 执行SQL并更新MYDS
  • 以MYDS为单位计算记录的数量
  • 如果MYDS计数没有更改,请停止执行
  • 否则,转到第三个

  • 我是SAS的新手(确切地说是3天),我试图把所有的东西都放在一起。(如果我在Java中这样做的话,我会使用上面的逻辑)

    这里有一个宏方法,它主要遵循您的逻辑,但首先转换您的数据,输入/输出是一个
    ID
    s列表(您可以很容易地通过它访问电子邮件)

    这段代码可能会介绍一些您不熟悉的SAS特性,但是下面的注释和解释应该会有所帮助。如果还有任何不清楚的地方,请查看链接或添加评论

    它需要输入数据:

    • inData
      :带有
      ID
      EMAIL*
      变量的表1
    • matched
      :已知通缉
      ID
      s的初始列表
    它返回:

    • matched
      :更新的通缉
      ID
      s列表

    /*将处理过程包装在宏中,以便我们可以使用%do循环*/
    %宏循环器(maxIter=5);
    /*将所有电子邮件放在一列中以简化比较*/
    proc transpose data=inData out=trans(rename=(col1=email));
    按身份证;
    var电子邮件:;
    跑
    /*初始化%where条件的计数*/
    %设_n匹配=0;
    %设nMatched=1;
    %设i=0;
    /*循环,直到没有添加新ID(或最大迭代次数)*/
    %执行%while(&u n匹配。<&n匹配。和&i<&maxIter.);
    %设_n匹配=&n匹配。;
    %设i=%eval(&i.+1);
    %注意事项:循环和i.:&N匹配。匹配的。;
    /*将匹配项移动到临时表*/
    proc datasets library=work nolist nowarn;
    删除匹配项;
    更改匹配=_匹配;
    退出
    /*获取新的匹配ID*/
    proc-sql-noprint;
    创建匹配为的表
    选择不同的c.ID
    从(u)匹配为
    左连接trans为b
    在a.ID=b.ID上
    左连接trans为c
    在b.email=c.email上;
    /*重新计数*/
    将计数(*)选择为:从匹配中选择匹配;
    退出
    %结束;
    %修理活套;
    %活套(最大值=10);
    
    有趣的是:

    • :将输入转换为一个深表,以便所有电子邮件地址都在一个变量中,这使得编写电子邮件比较逻辑更简单(所需重复次数更少),并将数据置于一种格式中,以便您在必要时更容易清理电子邮件地址(想想
      upcase()
      strip()
      ,等等)
    • %macro%mend
      :用于定义宏的语句。这是必要的,因为您不能在开放代码中使用宏逻辑或循环。我还添加了一个参数,以便您可以看到它是如何工作的
    • 和:两种创建方法。宏变量以前缀
      &
      引用,用于在执行SAS程序之前将文本插入其中
    • :在宏中执行循环的方法之一。将重复运行中的代码,直到条件的计算结果为false
    • :对数据集和库执行管理任务的过程。此处用于删除和重命名临时表

    您可能可以按照上面的设置编写宏,但我的一部分告诉您,如果重新构造数据,您可能会有更好的选择。你能举一个有效的例子吗?我很难想象这个问题。根据你数据的大小,哈希表可能是解决这类问题的好方法。
    /* Wrap the processing in a macro so that we can use a %do loop */
    %macro looper(maxIter = 5);
        /* Put all the emails in one column to make comparison simpler */
        proc transpose data = inData out = trans (rename = (col1 = email));
            by ID;
            var email:;
        run;        
        /* Initialise the counts for the %where condition */
        %let _nMatched = 0;
        %let nMatched = 1;
        %let i = 0;
        /* Loop until no new IDs are added (or maximum number of iterations) */
        %do %while(&_nMatched. < &nMatched. and &i < &maxIter.); 
            %let _nMatched = &nMatched.;
            %let i = %eval(&i. + 1);
            %put NOTE: Loop &i.: &nMatched. matched.;
            /* Move matches to a temporary table */
            proc datasets library = work nolist nowarn;
                delete _matched;
                change matched = _matched;
            quit;
            /* Get new matched IDs */
            proc sql noprint;
                create table matched as
                select distinct c.ID
                from _matched as a
                left join trans as b
                    on a.ID = b.ID
                left join trans as c
                    on b.email = c.email;
                /* Get new count */
                select count(*) into :nMatched from matched;
            quit;
        %end;
    %mend looper;
    %looper(maxIter = 10);