SAS PROC SQL:在IN()中硬编码值时,为什么会出现不同的错误?

SAS PROC SQL:在IN()中硬编码值时,为什么会出现不同的错误?,sas,proc-sql,Sas,Proc Sql,我对SAS PROC SQL中的以下两个代码有疑问 代码1:(标准图书版本) 代码2:(实践中更快的方法) 当我试图通过在#1中使用子查询来更优雅地执行此操作时,运行时间最多可达50分钟以上。但使用代码2,相同的输入在3分钟内返回。为什么呢?注意,使用内部连接()也同样慢。输入是5000+CLAIMID,我每天手动将其粘贴到IN(“…”)块中 附言:索赔是虚构的,在现实生活中是随机的 CLAIMID在DW.CLAIMS中被索引。我正在使用SAS PROC SQL访问Oracle数据库。发生了什么

我对SAS PROC SQL中的以下两个代码有疑问

代码1:(标准图书版本)

代码2:(实践中更快的方法)

当我试图通过在#1中使用子查询来更优雅地执行此操作时,运行时间最多可达50分钟以上。但使用代码2,相同的输入在3分钟内返回。为什么呢?注意,使用内部连接()也同样慢。输入是5000+CLAIMID,我每天手动将其粘贴到IN(“…”)块中

附言:索赔是虚构的,在现实生活中是随机的


CLAIMID在DW.CLAIMS中被索引。我正在使用SAS PROC SQL访问Oracle数据库。发生了什么事,有没有更好的办法?谢谢

我认为您正在处理本地数据和服务器上的数据。当SAS处理来自不同来源(数据库)的数据时,它会将所有数据都带入SAS进行处理,这可能非常非常缓慢

相反,您可以创建一个宏变量并在查询中使用它。如果是5000,则应将其放入一个宏变量中,假设每个宏变量的长度小于13个字符。宏变量的大小限制为64K个字符,因此它取决于变量的长度。如果没有,您可以创建一个宏

proc sql noprint;
   select quote(claimID, "'") into : claim_list separated by ", " from input;
quit;



proc sql;
CREATE TABLE WORK.OUTPUT AS 
SELECT 
"CLAIM" AS SOURCE,
a.CLAIMID, 
a.DXCODE
FROM  DW.CLAIMS_BAV AS a
WHERE 
a.SITEID = '0001'
AND a.CLAIMID IN (&claim_list.);
quit;

我不知道我能告诉你为什么SAS在第一次选择时这么慢;显然,在这种情况下有些东西没有得到优化

如果我不得不猜测的话,我猜SAS在第一种情况下决定它不能使用pass-through SQL,因此它下载整个大表,然后运行这个SAS端,而在第二种情况下,它将查询传递到SQL数据库,并且只将结果行传输回来

但无论如何,有几种方法可以解决这个问题。这里有一个:使用宏变量精确地执行粘贴操作

proc sql;
select quote(strip(claimid)) into :claimlist separated by ',' 
from work.input
;

CREATE TABLE WORK.OUTPUT AS 
SELECT 
    "CLAIM" AS SOURCE,
    a.CLAIMID, 
    a.DXCODE
FROM 
    DW.CLAIMS_BAV AS a
WHERE 
    a.SITEID = '0001'
    AND a.CLAIMID IN (&claimlist.)
;
quit;
Tada,你不必再碰这个了,它和你做的复制/粘贴是一样的

一些额外的注释给出了一些评论:

如果CLAIMID小于15,可能会有空格填充,所以我添加了
strip
来删除这些。对于字符串比较来说,这无关紧要——除了宏语言可能已经用完之外,我担心一些DBM可能实际上关心填充。如果15是一个恒定长度,您可以省略
条带

宏变量在空间中最多运行64K。如果有15个字符的变量加上“”两个字符加上逗号一,则有18个字符;您有超过3500个值的空间。不幸的是,这个数字不到5000

在这种情况下,您可以将字段拆分为两个宏变量(非常简单,希望使用
obs
firstobs
),也可以使用其他解决方案

  • 转移
    工作。将
    数据集输入
    DW
    libname,然后在那里执行SQL连接
  • 将claimID的内容放入文件而不是宏变量中,然后
    %include
    该文件
  • 使用
    callexecute
    执行整个过程SQL
  • 下面是调用执行的一个示例

    data _null_;
      set work.input end=eof;
      if _n_=1 then do;
        call execute('CREATE TABLE WORK.OUTPUT AS 
    SELECT 
        "CLAIM" AS SOURCE,
        a.CLAIMID, 
        a.DXCODE
    FROM 
        DW.CLAIMS_BAV AS a
    WHERE 
        a.SITEID = "0001"
        AND a.CLAIMID IN (');      *the part of the SQL query before the list of IDs;
      end;
      call execute(quote(claimID) || ' ');
      if EOF then do;
        call execute('); QUIT;');  *the part of the SQL query after the list of IDs;
      end;
    run;
    
    这与
    %INCLUDE
    解决方案几乎完全相同,只是在那里你将
    那些东西放到一个文本文件中,而不是
    调用并执行它,然后你
    %INCLUDE
    这个文本文件。

    在没有子查询的情况下使用In()肯定更快,但要记住的其他性能考虑是运行时的网络和计算服务器负载/流量;假设您在客户机/服务器配置上运行

    如果您计划使用SQL select into宏变量解决方案;请记住不同值的计数以及保存在宏中的字符串的长度,因为存在大小限制。

    请确保使用

    option sastrace=',,,ds' sastraceloc=saslog nostsuffix;
    
    接收有关SAS/ACESS引擎如何将代码转换为DB语句的信息

    为了给SAS一个提示,在(1,2,3,…)
    子句中从
    In(选择…
    query)中动态构建
    In(1,2,3,…)
    子句

  • MULTI\u DATASRC\u OPT=IN\u子句添加到
    libname DW…
    语句中,然后
  • dbmaster
    dataset选项添加到“master”表中
  • 类似于以下查询之一:

    CREATE TABLE WORK.OUTPUT AS 
    SELECT 
        "CLAIM" AS SOURCE,
        a.CLAIMID, 
        a.DXCODE
    FROM 
        DW.CLAIMS_BAV (dbmaster=yes) AS a
    WHERE 
        a.SITEID = '0001'
        AND a.CLAIMID IN (SELECT CLAIMID FROM WORK.INPUT)
    

    您还可以将In()值保存在表中,然后只进行连接

    PROC-SQL;
    /*索赔ID表*/
    创建表格WORK.OUTPUT1作为
    挑选
    “索赔”作为来源,
    a、 CLAIMID,
    a、 DXCODE
    从…起
    DW.BAV作为一个
    哪里
    a、 SITEID='0001';
    /*ID查找表*/
    创建表格WORK.OUTPUT2作为
    挑选
    将b.CLAIMID与WORK.INPUT区分为b
    ;
    /*内部联接表/AKA查找联接*/
    创建表格。最终为
    挑选
    a、 源代码,a.CLAIMID,a.DXCODE
    从WORK.OUTPUT1作为内部联接WORK.OUTPUT2作为b
    关于a.CLAIMID=b.CLAIMID
    ;
    退出
    
    1)首次运行后是否清除缓存?2) 根据lad的评论,文字比子查询(请发布执行计划)提供更好的计划。当您使用文字时,数据库可以使用一些技巧,例如创建一个带有值的散列,以便更快地查找。@lad2025在SAS中如何获取执行计划?DW libref是指向SAS数据集还是指向某个外部数据库?也许您已经迫使SAS将整个DW拉入SAS以与您的工作数据集连接,而不仅仅是将一些代码推入DW。删除和添加并不是一般SQL的问题,而是SAS特有的问题。伟大的人都有同感。。。SAS在通过查询时会正确转换引号,不是吗?(即,您可能不需要
    “”
    quote上的
    参数
    …是的,如果SAS为您将查询推入服务器,它将替换quote。SAS也不需要值列表中的逗号。您只需使用空格作为分隔符即可。谢谢Reeza,如果ClaimID的文本长度为15个字符。您知道(估计值)是多少吗不使用宏?65536/15=4399?…15字符进行限制
    option sastrace=',,,ds' sastraceloc=saslog nostsuffix;
    
    CREATE TABLE WORK.OUTPUT AS 
    SELECT 
        "CLAIM" AS SOURCE,
        a.CLAIMID, 
        a.DXCODE
    FROM 
        DW.CLAIMS_BAV (dbmaster=yes) AS a
    WHERE 
        a.SITEID = '0001'
        AND a.CLAIMID IN (SELECT CLAIMID FROM WORK.INPUT)
    
    CREATE TABLE WORK.OUTPUT AS 
    SELECT 
        "CLAIM" AS SOURCE,
        a.CLAIMID, 
        a.DXCODE
    FROM 
        DW.CLAIMS_BAV (dbmaster=yes) AS a
        inner join WORK.INPUT AS b
        on a.CLAIMID = b.CLAIMID
    WHERE 
        a.SITEID = '0001'