SAS 升级到 9.4M7 后,Oracle 数据库上的 MIN 函数行为发生了变化
MIN function behavior changed on Oracle databases after SAS Upgrade to 9.4M7
我有一个已经运行多年的程序。今天,我们从 SAS 9.4M3 升级到 9.4M7。
proc setinit
Current version: 9.04.01M7P080520
从那以后,我无法获得与升级前相同的结果。
请注意,我是直接在 Oracle 数据库上查询。
尝试用最小的、可重现的 SAS table 示例复制问题,我发现在 SAS table 而不是 Oracle 数据库上查询时问题消失了。
假设我有以下数据集:
data have;
infile datalines delimiter="|";
input name :. id . value :. t1 :.;
datalines;
Joe|A|TLO
Joe|B|IKSK
Joe|C|Yes
;
使用临时 table:
proc sql;
create table want as
select name,
min(case when id = "A" then value else "" end) as A length 8
from have
group by name;
quit;
Results:
name A
Joe TLO
但是,当 运行 直接在 oracle 数据库上进行完全相同的查询时,我得到了一个缺失值:
proc sql;
create table want as
select name,
min(case when id = "A" then value else "" end) as A length 8
from have_oracle
group by name;
quit;
name A
Joe
根据文档,min()
函数在 SAS table
上使用时运行正常
The MIN function returns a missing value (.) only if all arguments are missing.
我相信当 Oracle 不理解 SAS 传递给它的函数时会发生这种情况 - SAS 和 Oracle 中的最小函数非常不同,SAS 中的等价函数是 LEAST()
.
所以我的猜测是升级弄乱了 t运行如何将 SAS min 函数转换为 Oracle,但这仍然是一个猜测。有人 运行 喜欢这种行为吗?
编辑:@Richard 的评论
options sastrace=',,,d' sastraceloc=saslog nostsuffix;
proc sql;
create table want as
select t1.name,
min(case when id = 'A' then value else "" end) as A length 8
from oracle_db.names t1 inner join oracle_db.ids t2 on (t1.tid = t2.tid)
group by t1.name;
ORACLE_26: Prepared: on connection 0
SELECT * FROM NAMES
ORACLE_27: Prepared: on connection 1
SELECT UI.INDEX_NAME, UIC.COLUMN_NAME FROM USER_INDEXES UI,USER_IND_COLUMNS UIC WHERE UI.TABLE_NAME='NAMES' AND
UIC.TABLE_NAME='NAMES' AND UI.INDEX_NAME=UIC.INDEX_NAME
ORACLE_28: Executed: on connection 1
SELECT statement ORACLE_27
ORACLE_29: Prepared: on connection 0
SELECT * FROM IDS
ORACLE_30: Prepared: on connection 1
SELECT UI.INDEX_NAME, UIC.COLUMN_NAME FROM USER_INDEXES UI,USER_IND_COLUMNS UIC WHERE UI.TABLE_NAME='IDS' AND
UIC.TABLE_NAME='IDS' AND UI.INDEX_NAME=UIC.INDEX_NAME
ORACLE_31: Executed: on connection 1
SELECT statement ORACLE_30
ORACLE_32: Prepared: on connection 0
select t1."NAME", MIN(case when t2."ID" = 'A' then t1."VALUE" else ' ' end) as A from
NAMES t1 inner join IDS t2 on t1."TID" = t2."TID" group by t1."NAME"
ORACLE_33: Executed: on connection 0
SELECT statement ORACLE_32
ACCESS ENGINE: SQL statement was passed to the DBMS for fetching data.
NOTE: Table WORK.SELECTED_ATTR created, with 1 row and 2 columns.
! quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.34 seconds
cpu time 0.09 seconds
当我尝试在 Oracle 中重现它时,我得到了您正在寻找的结果,所以我怀疑它与 SAS(我不熟悉)有关。
with t as (
select 'Joe' name, 'A' id, 'TLO' value from dual union all
select 'Joe' name, 'B' id, 'IKSK' value from dual union all
select 'Joe' name, 'C' id, 'Yes' value from dual
)
select name
, min(case when id = 'A' then value else '' end) as a
from t
group by name;
NAME A
---- ----
Joe TLO
不相关,如果您只对 id = 'A' 感兴趣,那么更好的查询是:
select name
, min(value) as a
from t
where id = 'A'
group by name;
使用 SASTRACE=
系统选项记录发送到 DBMS 的 SQL 语句。
options SASTRACE=',,,d';
将提供最详细的日志记录。
从准备好的语句中,您可以看到为什么从 Oracle 查询中得到空白。
select
t1."NAME"
, MIN ( case
when t2."ID" = 'A' then t1."VALUE"
else ' '
end
) as A
from
NAMES t1 inner join IDS t2 on t1."TID" = t2."TID"
group by
t1."NAME"
SQL MIN () 聚合函数将不考虑空值。
在 SAS SQL 中,空白值也被解释为 null。
在 SAS 中,您的 SQL 查询 returns 最小非空值 TLO
在 Oracle 转换查询中,SAS 空白 ''
被转换为 ' '
一个非空的空白字符,因此 ' ' < 'TLO'
得到空白结果.
您要在 Oracle 中强制执行的实际 MIN 是 min(case when id = "A" then value else null end)
,@Tom 已经表明可以通过省略 else 子句来实现。
查看实际差异的唯一方法是 运行 先前 SAS 版本中带有跟踪的查询,或者如果幸运的话,请参阅(被许多人忽略的)“新功能”文档中的解释。
为什么使用 ' '
或 ''
作为 ELSE 值?也许 Oracle 以不同于空字符串的方式处理其中包含空格的字符串。
为什么不在 ELSE 子句中使用 null
?
或者只是离开 ELSE 子句并让它默认为 null
?
libname mylib oracle .... ;
proc sql;
create table want as
select name
, min(case when id = "A" then value else null end) as A length 8
from mylib.have_oracle
group by name
;
quit;
也可以自己尝试 运行 Oracle 代码,而不是使用隐式传递。
proc sql;
connect to oracle ..... ;
create table want as
select * from connection to oracle
(
select name,
min(case when id = "A" then value else null end) as A length 8
from have_oracle
group by name
)
;
quit;
我有一个已经运行多年的程序。今天,我们从 SAS 9.4M3 升级到 9.4M7。
proc setinit
Current version: 9.04.01M7P080520
从那以后,我无法获得与升级前相同的结果。
请注意,我是直接在 Oracle 数据库上查询。
尝试用最小的、可重现的 SAS table 示例复制问题,我发现在 SAS table 而不是 Oracle 数据库上查询时问题消失了。
假设我有以下数据集:
data have;
infile datalines delimiter="|";
input name :. id . value :. t1 :.;
datalines;
Joe|A|TLO
Joe|B|IKSK
Joe|C|Yes
;
使用临时 table:
proc sql;
create table want as
select name,
min(case when id = "A" then value else "" end) as A length 8
from have
group by name;
quit;
Results:
name A
Joe TLO
但是,当 运行 直接在 oracle 数据库上进行完全相同的查询时,我得到了一个缺失值:
proc sql;
create table want as
select name,
min(case when id = "A" then value else "" end) as A length 8
from have_oracle
group by name;
quit;
name A
Joe
根据文档,min()
函数在 SAS table
The MIN function returns a missing value (.) only if all arguments are missing.
我相信当 Oracle 不理解 SAS 传递给它的函数时会发生这种情况 - SAS 和 Oracle 中的最小函数非常不同,SAS 中的等价函数是 LEAST()
.
所以我的猜测是升级弄乱了 t运行如何将 SAS min 函数转换为 Oracle,但这仍然是一个猜测。有人 运行 喜欢这种行为吗?
编辑:@Richard 的评论
options sastrace=',,,d' sastraceloc=saslog nostsuffix;
proc sql;
create table want as
select t1.name,
min(case when id = 'A' then value else "" end) as A length 8
from oracle_db.names t1 inner join oracle_db.ids t2 on (t1.tid = t2.tid)
group by t1.name;
ORACLE_26: Prepared: on connection 0
SELECT * FROM NAMES
ORACLE_27: Prepared: on connection 1
SELECT UI.INDEX_NAME, UIC.COLUMN_NAME FROM USER_INDEXES UI,USER_IND_COLUMNS UIC WHERE UI.TABLE_NAME='NAMES' AND
UIC.TABLE_NAME='NAMES' AND UI.INDEX_NAME=UIC.INDEX_NAME
ORACLE_28: Executed: on connection 1
SELECT statement ORACLE_27
ORACLE_29: Prepared: on connection 0
SELECT * FROM IDS
ORACLE_30: Prepared: on connection 1
SELECT UI.INDEX_NAME, UIC.COLUMN_NAME FROM USER_INDEXES UI,USER_IND_COLUMNS UIC WHERE UI.TABLE_NAME='IDS' AND
UIC.TABLE_NAME='IDS' AND UI.INDEX_NAME=UIC.INDEX_NAME
ORACLE_31: Executed: on connection 1
SELECT statement ORACLE_30
ORACLE_32: Prepared: on connection 0
select t1."NAME", MIN(case when t2."ID" = 'A' then t1."VALUE" else ' ' end) as A from
NAMES t1 inner join IDS t2 on t1."TID" = t2."TID" group by t1."NAME"
ORACLE_33: Executed: on connection 0
SELECT statement ORACLE_32
ACCESS ENGINE: SQL statement was passed to the DBMS for fetching data.
NOTE: Table WORK.SELECTED_ATTR created, with 1 row and 2 columns.
! quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.34 seconds
cpu time 0.09 seconds
当我尝试在 Oracle 中重现它时,我得到了您正在寻找的结果,所以我怀疑它与 SAS(我不熟悉)有关。
with t as (
select 'Joe' name, 'A' id, 'TLO' value from dual union all
select 'Joe' name, 'B' id, 'IKSK' value from dual union all
select 'Joe' name, 'C' id, 'Yes' value from dual
)
select name
, min(case when id = 'A' then value else '' end) as a
from t
group by name;
NAME A
---- ----
Joe TLO
不相关,如果您只对 id = 'A' 感兴趣,那么更好的查询是:
select name
, min(value) as a
from t
where id = 'A'
group by name;
使用 SASTRACE=
系统选项记录发送到 DBMS 的 SQL 语句。
options SASTRACE=',,,d';
将提供最详细的日志记录。
从准备好的语句中,您可以看到为什么从 Oracle 查询中得到空白。
select
t1."NAME"
, MIN ( case
when t2."ID" = 'A' then t1."VALUE"
else ' '
end
) as A
from
NAMES t1 inner join IDS t2 on t1."TID" = t2."TID"
group by
t1."NAME"
SQL MIN () 聚合函数将不考虑空值。
在 SAS SQL 中,空白值也被解释为 null。
在 SAS 中,您的 SQL 查询 returns 最小非空值 TLO
在 Oracle 转换查询中,SAS 空白 ''
被转换为 ' '
一个非空的空白字符,因此 ' ' < 'TLO'
得到空白结果.
您要在 Oracle 中强制执行的实际 MIN 是 min(case when id = "A" then value else null end)
,@Tom 已经表明可以通过省略 else 子句来实现。
查看实际差异的唯一方法是 运行 先前 SAS 版本中带有跟踪的查询,或者如果幸运的话,请参阅(被许多人忽略的)“新功能”文档中的解释。
为什么使用 ' '
或 ''
作为 ELSE 值?也许 Oracle 以不同于空字符串的方式处理其中包含空格的字符串。
为什么不在 ELSE 子句中使用 null
?
或者只是离开 ELSE 子句并让它默认为 null
?
libname mylib oracle .... ;
proc sql;
create table want as
select name
, min(case when id = "A" then value else null end) as A length 8
from mylib.have_oracle
group by name
;
quit;
也可以自己尝试 运行 Oracle 代码,而不是使用隐式传递。
proc sql;
connect to oracle ..... ;
create table want as
select * from connection to oracle
(
select name,
min(case when id = "A" then value else null end) as A length 8
from have_oracle
group by name
)
;
quit;