SAS PROC SQL:在IN()中硬编码值时,为什么会出现不同的错误?
我对SAS PROC SQL中的以下两个代码有疑问 代码1:(标准图书版本) 代码2:(实践中更快的方法) 当我试图通过在#1中使用子查询来更优雅地执行此操作时,运行时间最多可达50分钟以上。但使用代码2,相同的输入在3分钟内返回。为什么呢?注意,使用内部连接()也同样慢。输入是5000+CLAIMID,我每天手动将其粘贴到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数据库。发生了什么
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连接%include
该文件callexecute
执行整个过程SQLdata _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'