SAS PROC SQL:为什么 IN() 中的硬编码值不同?
SAS PROC SQL: why there is a different when hard coding values within IN()?
我对 SAS PROC SQL 中的以下 2 个代码有疑问。
代码 1:(标准图书版本)
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 (SELECT CLAIMID FROM WORK.INPUT)
代码 2:(实践中速度更快的方法)
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 ('10001', '10002', '10003', ... '15000')
当我尝试在#1 中使用子查询更优雅地完成它时,运行 时间增加了 50 分钟以上。但是使用代码 2 在 3 分钟内输入相同的 returns。这是为什么?请注意,使用 INNER JOIN 也一样慢 (after reading this)。输入是 5000+ CLAIMID,我每天手动粘贴到 IN('...') 块中。
PS:CLAIMID是编造的,现实生活中是随机的。
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 一开始这么慢 select;在那种情况下显然没有优化某些东西。
如果非要我猜的话,我猜 SAS 在第一种情况下决定它不能使用直通 SQL,所以它正在下载整个 table 和然后 运行 连接这个 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,你不用再碰这个了,它和你碰过的copy/paste一样。
一些额外的注释给出了一些评论:
如果 CLAIMID 小于 15,您可能有 space 填充,所以我添加了 strip
来删除它们。字符串比较并不重要——除非你可能 运行 不使用宏语言,而且我担心某些 DBMS 可能实际上关心填充。如果 15 是恒定长度,则可以省略 strip
。
宏变量 运行 在 space 中最大 64K。如果你有 15 个字符的变量加上“”二加逗号一,你就有 18 个字符;您有超过 3500 个值的空间。不幸的是,这还不到 5000。
在这种情况下,您可以将字段拆分为两个宏变量(希望足够简单,使用 obs
和 firstobs
)或者您可以做一些其他的解决方案。
- 将
work.input
数据集转移到 DW
库名中,然后在 SQL 中进行连接。
- 将 claimID 的内容放入文件而不是放入宏变量,然后
%include
该文件。
- 使用
call execute
执行整个过程SQL.
这是 CALL EXECUTE 的一个例子。
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
解决方案几乎相同,只是您 put
将这些内容写入文本文件而不是 CALL EXECUTE
,然后您 %INCLUDE
那个文本文件。
使用不带子查询的 In() 肯定更快,但要记住的其他性能考虑因素是 运行 时的网络和计算服务器 load/traffic;假设您 运行 在客户端/服务器配置上。
如果打算使用SQL select 宏变量解决方案;请记住不同值的计数和您在宏中保存的字符串的长度,因为存在大小限制。
请务必使用
option sastrace=',,,ds' sastraceloc=saslog nostsuffix;
接收有关 SAS/Aceess 引擎如何将您的代码转换为数据库语句的信息。
为了给 SAS 提示从你的 IN (SELECT ..
查询动态构建 IN (1,2,3, ..)
子句
- 将
MULTI_DATASRC_OPT=IN_CLAUSE
添加到您的 libname DW ...
声明和
- 将
dbmaster
数据集选项添加到 "master" table
喜欢以下查询之一:
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'
您还可以将 In() 值保存在 table 中,然后只进行连接。
PROC SQL;
/*CLAIM ID Table*/
CREATE TABLE WORK.OUTPUT1 AS
SELECT
"CLAIM" AS SOURCE,
a.CLAIMID,
a.DXCODE
FROM
DW.CLAIMS_BAV AS a
WHERE
a.SITEID = '0001';
/*ID Lookup Table*/
CREATE TABLE WORK.OUTPUT2 AS
SELECT
DISTINCT b.CLAIMID FROM WORK.INPUT AS b
;
/*Inner Join Table / AKA lookup join*/
CREATE TABLE WORK.Final AS
SELECT
a.SOURCE, a.CLAIMID, a.DXCODE
FROM WORK.OUTPUT1 AS a INNER JOIN WORK.OUTPUT2 AS b
ON a.CLAIMID = b.CLAIMID
;
QUIT;
我对 SAS PROC SQL 中的以下 2 个代码有疑问。
代码 1:(标准图书版本)
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 (SELECT CLAIMID FROM WORK.INPUT)
代码 2:(实践中速度更快的方法)
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 ('10001', '10002', '10003', ... '15000')
当我尝试在#1 中使用子查询更优雅地完成它时,运行 时间增加了 50 分钟以上。但是使用代码 2 在 3 分钟内输入相同的 returns。这是为什么?请注意,使用 INNER JOIN 也一样慢 (after reading this)。输入是 5000+ CLAIMID,我每天手动粘贴到 IN('...') 块中。
PS:CLAIMID是编造的,现实生活中是随机的。
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 一开始这么慢 select;在那种情况下显然没有优化某些东西。
如果非要我猜的话,我猜 SAS 在第一种情况下决定它不能使用直通 SQL,所以它正在下载整个 table 和然后 运行 连接这个 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,你不用再碰这个了,它和你碰过的copy/paste一样。
一些额外的注释给出了一些评论:
如果 CLAIMID 小于 15,您可能有 space 填充,所以我添加了 strip
来删除它们。字符串比较并不重要——除非你可能 运行 不使用宏语言,而且我担心某些 DBMS 可能实际上关心填充。如果 15 是恒定长度,则可以省略 strip
。
宏变量 运行 在 space 中最大 64K。如果你有 15 个字符的变量加上“”二加逗号一,你就有 18 个字符;您有超过 3500 个值的空间。不幸的是,这还不到 5000。
在这种情况下,您可以将字段拆分为两个宏变量(希望足够简单,使用 obs
和 firstobs
)或者您可以做一些其他的解决方案。
- 将
work.input
数据集转移到DW
库名中,然后在 SQL 中进行连接。 - 将 claimID 的内容放入文件而不是放入宏变量,然后
%include
该文件。 - 使用
call execute
执行整个过程SQL.
这是 CALL EXECUTE 的一个例子。
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
解决方案几乎相同,只是您 put
将这些内容写入文本文件而不是 CALL EXECUTE
,然后您 %INCLUDE
那个文本文件。
使用不带子查询的 In() 肯定更快,但要记住的其他性能考虑因素是 运行 时的网络和计算服务器 load/traffic;假设您 运行 在客户端/服务器配置上。
如果打算使用SQL select 宏变量解决方案;请记住不同值的计数和您在宏中保存的字符串的长度,因为存在大小限制。
请务必使用
option sastrace=',,,ds' sastraceloc=saslog nostsuffix;
接收有关 SAS/Aceess 引擎如何将您的代码转换为数据库语句的信息。
为了给 SAS 提示从你的 IN (SELECT ..
查询动态构建 IN (1,2,3, ..)
子句
- 将
MULTI_DATASRC_OPT=IN_CLAUSE
添加到您的libname DW ...
声明和 - 将
dbmaster
数据集选项添加到 "master" table
喜欢以下查询之一:
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'
您还可以将 In() 值保存在 table 中,然后只进行连接。
PROC SQL;
/*CLAIM ID Table*/
CREATE TABLE WORK.OUTPUT1 AS
SELECT
"CLAIM" AS SOURCE,
a.CLAIMID,
a.DXCODE
FROM
DW.CLAIMS_BAV AS a
WHERE
a.SITEID = '0001';
/*ID Lookup Table*/
CREATE TABLE WORK.OUTPUT2 AS
SELECT
DISTINCT b.CLAIMID FROM WORK.INPUT AS b
;
/*Inner Join Table / AKA lookup join*/
CREATE TABLE WORK.Final AS
SELECT
a.SOURCE, a.CLAIMID, a.DXCODE
FROM WORK.OUTPUT1 AS a INNER JOIN WORK.OUTPUT2 AS b
ON a.CLAIMID = b.CLAIMID
;
QUIT;