SAS PROC SQL:如何快速搜索变量是否包含完整子字符串?

时间:2018-04-30 15:02:54

标签: sas proc-sql

我在工作中遇到这样的问题:

Code的值类似于1000,1200,A1000,B1200,AAA,BBB等。目前它由空格分隔,有时由于数据输入不佳而多于一个空格。我正在尝试检查记录是否包含我感兴趣的代码。

Interested_Code:1000或A1000或444或555或A555等。

我知道一个简单的解决方案from this answer

A.CODE LIKE CAT('% ', T3.Interested_Code, ' %')

我已将前导和尾随空格附加到A.CODE,以确保返回“完整”完全匹配。因为如果我只是做

A.CODE LIKE CAT('%', T3.Interested_Code, '%') or
A.CODE CONTAINS T3.Interested_Code

我会在包含代码= 1000的行中得到代码= A1000的误报,这会匹配部分代码,但不一定是正确的结果。

我的代码在上面工作,但它做了太多的测试并且非常慢。在PROC SQL中有更快或更智能的方法吗?主表大约是100k行,每行大约有10-20个代码。感兴趣的代码大约是8k值。感谢。

3 个答案:

答案 0 :(得分:3)

您可以使用FINDWINDEXW来查找"字" (默认情况下,用空格或类似的东西分隔)。这可能比你的解决方案更好,特别是因为你找不到

"1000 "

因为它不以空格开头,就像你一样。

proc sql;
  create table final_codes as
  select codes.*
  from codes where exists (
    select 1 from interested_codes
    where findw(codes.code,trim(interested_codes.code)) > 0)
  ;
quit;

但是,这实际上是一个笛卡尔联接,非常慢。它必须加入所有可能的组合 - 8000次100,000,或有效8亿次临时行,然后再进行子集化。无论你做什么,它都不会那么快。

在数据步骤中执行此操作会更有效,特别是因为一旦找到匹配项就可以更轻松地停止。您可以将interested_codes表放入哈希表或临时数组中,然后根据您的匹配频率,可能会更快地搜索interested_codes表中的每个代码,或者相反,但两种方式都停止当你找到一个匹配(而不是做所有可能的组合)。

答案 1 :(得分:2)

尝试使用正则表达式:

data want;
   set have;
   where prxmatch('/^1000$/',strip(code));
run;

答案 2 :(得分:2)

有两个主要问题

  1. 代码数据可能包含字母和数字之间的空格
  2. 搜索空间超过多个目标代码
  3. 解决方案的功能将是

    1. 规范化代码数据
    2. 搜索任何目标代码
       2A。在匹配任何的情况下标记记录  2B。阐明哪些目标匹配(每个目标代码的二进制变量,或每个匹配目标的结果行)
    3. 您必须对安装进行基准测试,以便在各种方法之间进行性能比较。

      此示例代码是2a的模型。宏构建一个SQL案例来标记“任何匹配”条件。结果查询很昂贵,因为它需要为每行标准化正则表达式,并且所有情况条件必须失败才能从结果集中排除行。

      data have;
        code = 'A    1000 1111 C333 555 A111 Z  999 B 222'; output;
        code = 'ZZZZZ 1121'; output;
        code = 'A    1000'; output;
        code = 'AB1000'; output;
      run;
      
      %macro withAnyOf (data=have, out=want, targets=);
      
        %local i qTarget N;
        %let N = %sysfunc(countw(&targets,%str( )));
      
      %put NOTE: &=N;
      
        %do i = 1 %to &N;
          %local clause&i;
      
          %let qTarget = %sysfunc(quote(%qscan(&targets,&i,%str( ))));
          %let clause&i = when indexw (calculated codeCleaned, &qTarget) then 1;
      
          %put NOTE: &&clause&i;
        %end;
      
        proc sql;
          create table &out(drop=codeCleaned) as
          select 
            *
          , ' ' || prxchange('s/([A-Z]) +/$1/',-1,code) || ' ' as codeCleaned
          from &data
          where
            case
              %do i = 1 %to &N;
              &&clause&i
              %end;        
              else 0
            end
          ;
        quit;
      
      %mend;
      
      options mprint;
      
      %withAnyOf (targets=1000 A1000 444 555 A555)