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

SAS PROC SQL: How to quickly search if a variable contain a full substring?

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

Code的值如1000、1200、A1000、B1200、AAA、BBB等。目前它被spaces分隔,由于数据不佳,有时会超过一个输入。我正在尝试检查记录是否包含我感兴趣的代码。

Interested_Code: 1000 or A1000 or 444 or 555 or A555 etc.

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

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

我在 A.CODE 后附加了前导和尾随 space 以确保返回 "full" 完全匹配。因为如果我只是做

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

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

我的代码可以在上面运行,但是它进行了太多测试并且非常慢。 PROC SQL 有没有更快或更聪明的方法?主要的 table 大约有 100k 行,每行大约有 10-20 个代码。感兴趣的代码大约有 8k 个值。谢谢。

您可以使用 FINDWINDEXW,它们会找到 "words"(默认情况下,用 space 或类似的分隔)。这可能比您的解决方案更好,特别是因为您找不到

"1000 "

因为它不是以 space 开头的,所以您这样做的方式。

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 table 放入散列 table 或临时数组中,然后根据您的匹配频率搜索每个 code[=24 可能会更快=] 在 interested_codes table 中,或相反,但无论哪种方式在找到匹配项时停止(而不是进行所有可能的组合)。

尝试使用正则表达式:

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

主要有两个问题

  1. 代码数据可能包含字母和数字之间的space
  2. 搜索space超过了几个目标代码

解决方案的特点是

  1. 规范化代码数据
  2. 搜索任何目标代码
    2a.将记录标记为 匹配任何
    2b.阐明匹配的目标(每个目标代码的二进制变量,或每个匹配目标的结果行)

您必须对您的安装进行基准测试,以比较各种方法之间的性能。

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

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,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)