为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。
【DM版本】:--03134284132-20231226-213242-20081
【操作系统】:WIN10
【CPU】:X64
【问题描述】*:
测试中遇到个问题,对于CASE WHEN /DECODE 这两个根据条件做分支处理的控制语句/函数,当只有一个条件分支时,处理控制与多分支不一致
试验过程如下:
首先定义测试函数,用于输出测试值
CREATE OR REPLACE FUNCTION F_SHOW(V_VAL INT)
RETURN INT
IS
BEGIN
PRINT(V_VAL); --打印输出传入参数值,用于判断函数是否被执行
RETURN V_VAL;
END;
CASE WHEN 分支测试
--条件分支方式
--下面语句输出1,不满足条件的分支上函数也被执行
SELECT CASE WHEN 1 = 2 THEN F_SHOW(1) END FROM DUAL;
--下面两句输出 2 1,真假分支上的F_SHOW函数都被执行了
SELECT CASE WHEN 1 = 1 THEN F_SHOW(1) ELSE F_SHOW(2) END FROM DUAL;
SELECT CASE WHEN 1 = 2 THEN F_SHOW(1) ELSE F_SHOW(2) END FROM DUAL;
--下面这句执行正确,只执行了正确条件分支上的F_SHOW函数
--输出 1
SELECT CASE WHEN 1 = 1 THEN F_SHOW(1) WHEN 1 = 2 THEN F_SHOW(2) ELSE F_SHOW(3) END FROM DUAL;
值分支方式
--下面语句输出1,不满足条件的分支上函数也被执行
SELECT CASE 1 WHEN 2 THEN F_SHOW(1) END FROM DUAL;
--下面这个写法输出 2 1,两分支上F_SHOW函数都被执行
SELECT CASE 1 WHEN 1 THEN F_SHOW(1) ELSE F_SHOW(2) END FROM DUAL;
--下面这句结果正确,输出 1,只执行了匹配值分支上的F_SHOW函数
SELECT CASE 1 WHEN 1 THEN F_SHOW(1) WHEN 2 THEN F_SHOW(2) ELSE F_SHOW(3) END FROM DUAL;
DECODE 分支测试
--下面语句输出1,不满足条件的分支上函数也被执行
SELECT DECODE(1,2,F_SHOW(1)) FROM DUAL;
--输出 2 1,两分支上F_SHOW函数都被执行
SELECT DECODE(1,1,F_SHOW(1),F_SHOW(2)) FROM DUAL;
--下面两句执行正确,只执行了匹配值分支上的F_SHOW函数
--输出 1
SELECT DECODE(1,1,F_SHOW(1),2,F_SHOW(2)) FROM DUAL;
--输出 2
SELECT DECODE(2,1,F_SHOW(1),2,F_SHOW(2),F_SHOW(3)) FROM DUAL;
由测试过程可见,当处于单个条件判断时,真假分支上的函数都会被执行。这个与预期不符,在程序调用中容易引发缺陷。
且由于单分支判断较为常见,这个问题可能对性能造成影响。
换一个测试方法吧,前面用“输出”的方式在理解上确实容易与“查询结果”混淆。
下面测试方式是把执行过程通过函数记录到表中。
正常情况下,函数以数值计算为主,所以这个问题只是引起无效计算,影响性能。但如果分支上调用的函数通过自治事务进行DML操作,那可能会引发错误数据的产生。
这个还请关注。
--过程记录表
CREATE TABLE IF NOT EXISTS T_LOG
(
LOGINFO INT
);
--通过自治事务记录执行过程用的函数
CREATE OR REPLACE FUNCTION F_LOG(V_VAL INT)
RETURN INT
IS
PRAGMA AUTONOMOUS_TRANSACTION; --自治事务
BEGIN
PRINT(V_VAL); --打印输出传入参数值,用于判断函数是否被执行
--函数内有DML操作
INSERT INTO T_LOG(LOGINFO) VALUES(V_VAL);
COMMIT;
RETURN V_VAL;
END;
--首先清理记录表数据
TRUNCATE TABLE T_LOG;
--进行条件分支查询,并记录执行过程
SELECT CASE WHEN 1 = 1 THEN F_LOG(1) ELSE F_LOG(2) END FROM DUAL;
--查看记录表数据
SELECT * FROM T_LOG;
究其根本是 case when被预处理,默认情况下是为了走批量查询返回。
你可以关闭参数case_when_cvt_ifun = 0
alter system set 'case_when_cvt_ifun' = 0 both;
我测试是正常的呢

