CREATEOR REPLACE FUNCTION ISNUMERIC (STR IN VARCHAR2) RETURN NUMBER IS V_STR FLOAT; BEGIN IF STR ISNULL THEN RETURN0; ELSE BEGIN SELECT TO_NUMBER (STR) INTO V_STR FROM DUAL; EXCEPTION WHEN INVALID_NUMBER THEN RETURN0; END; RETURN1; END IF; END ISNUMERIC;
Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter(TO_NUMBER("TEST_RESULT")>100 AND "ISNUMERIC"("TEST_RESULT")=1)
无论条件顺序怎么写,均是先执行范围的判断,再执行ISNUMERIC函数,函数的执行优先级都在最后。
加一层或使用查询临时表
首先想到的方案,是否可以先将是数值结果的查询出来,然后再在这个基础上判断结果的范围。
1 2
select*from (select*from test_table where isnumeric(test_result) =1) where to_number(test_result) >100; with temp as (select*from test_table where isnumeric(test_result) =1) select*from temp where to_number(test_result) >100;
Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter(TO_NUMBER("TEST_TABLE"."TEST_RESULT")>100 AND "ISNUMERIC"("TEST_RESULT")=1)
使用视图
首先我们可以确认的是:
1
select*from test_table where isnumeric(test_result) =1;
Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter(TO_NUMBER("TEST_RESULT")>100 AND "ISNUMERIC"("TEST_RESULT")=1)
同事刚好看到我处理的问题,然后提出他处理过类似的字符串类型作为数值型,进行范围的判断的报表,不过他使用的方法是case when then。分两次查询,如果字符串可以转换为数字,那么返回这个字符串本身,否则返回一个数字。
那么我们将该方案使用decode函数来实现:
1 2 3 4 5 6 7 8
select* from (select test_id, decode(isnumeric(test_result), 1, to_number(test_result), null) test_result_num from test_table) where test_result_num >100;
Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter(DECODE("ISNUMERIC"("TEST_RESULT"),1,TO_NUMBER("TEST_RESULT"),NULL)>100)
Note ----- - dynamic sampling used for this statement
执行是将decode函数返回的结果再做比较,所以不会存在以上问题。
如果我们需要对以上数据做更新,可以这样写SQL语句:
1 2 3 4 5 6 7 8 9 10 11
update test_table set test_result ='阳性' where test_id in (select test_id from (select test_id, decode(isnumeric(test_result), 1, to_number(test_result), null) test_result_num from test_table) where test_result_num >100);