Oracle decode()函数返回值问题

 

今天碰到一个问题,说min函数返回错误的取值。详细如下:一个数据表列类型为number(6,2),其中有三行记录,分别为0,0.6和1。用min获取最小值,得到0.6

 

min是Oracle SQL的一个基础函数,理论上不会出现这样的Bug之类的。下面一起来模拟下实验环境。

 

 

1、环境构建

 

在实验数据库Oracle 11g环境下,构建实验数据表t。填入实验数据。

 

 

SQL> create table t (num number(6,2));

Table created

 

SQL> insert into t values (0);

1 row inserted

 

SQL> insert into t values (0.6);

1 row inserted

 

SQL> insert into t values (1);

1 row inserted

 

SQL> commit;

Commit complete

 

SQL> select * from t;

     NUM

--------

    0.00

    0.60

1.00

 

 

实验

 

 

SQL> select min(num) from t;

 

  MIN(NUM)

----------

         0

 

SQL> select min(to_number(num)) from t;

 

MIN(TO_NUMBER(NUM))

-------------------

                  0

 

 

没有什么问题,来看看下面这种写法:

 

 

SQL> select min(decode(num,-1,null,num)),min(num) from t;

 

MIN(DECODE(NUM,-1,NULL,NUM))               MIN(NUM)

---------------------------------------- ----------

.6                                                0

 

 

果然,诡异的现象发生了。

 

2、问题分析

 

一时间还是很唬人的,那么我们先抛开min函数,单独看数据列情况。抽丝剥茧吧。

 

 

SQL> select decode(num,-1,null,num),num from t;

 

DECODE(NUM,-1,NULL,NUM)                       NUM

---------------------------------------- --------

0                                            0.00

.6                                           0.60

1                                            1.00

 

 

这里只剩下一个decode函数的使用。从含义上看,当num为-1的时候,返回null值,否则就是原有的num值。但是有两个疑点,首先是0.60是如何转变为.6的呢?其次就是decode函数处理列的列对其方式,数字类型默认是右对齐,只有字符串是左对齐的。难道说经过decode函数处理之后,返回值变成了字符串?

 

那么,如果decode处理之后,变成了字符串的话,我们调整一个decode的结构,看看是否是由于处理变成字符串造成了问题。

 

 

SQL> select min(to_number(decode(num,-1,null,num))),min(num) from t;

 

MIN(TO_NUMBER(DECODE(NUM,-1,NU   MIN(NUM)

------------------------------ ----------

                             0          0

 

 

看来原因就在于decode函数使用处理之后,返回数据列是一个字符串类型。但是decode函数命名指定了num列,返回值是什么类型呢?

 

num是数字肯定没有什么问题?难道说疑点出现在null的返回值类型上?继续实验。

 

SQL> select decode(num,-1,num,num),num from t;

 

DECODE(NUM,-1,NUM,NUM)      NUM

---------------------- --------

                     0     0.00

                   0.6     0.60

                     1     1.00

 

 

果然,临时取消掉null,decode返回类型就正常。看来真是受到了null的影响。这个时候,笔者思考一个问题,Oracle Decode函数如何确定返回值类型列呢?

 

 

SQL> select decode(num,-1,'d',num),num from t;

 

DECODE(NUM,-1,'D',NUM)                        NUM

---------------------------------------- --------

0                                            0.00

.6                                           0.60

1                                            1.00

 

SQL> select decode(num,-1,'k',num),to_char(num) from t;

 

DECODE(NUM,-1,'K',NUM)                   TO_CHAR(NUM)

---------------------------------------- ----------------------------------------

0                                        0

.6                                       .6

1                                        1

 

 

上面的实验,让我们得出了和null值是相同的效果。这样,我们对decode有下面猜想:

 

ü        Oracle在调用decode函数的时候,是需要预先确定列的类型,因为毕竟出现在相同的列上;

ü        确定decode返回值类型,是依据参数中第一个条件返回类型。之后所有的返回类型都依据第一个类型进行强制类型转换;

ü        Oracle在第一个条件返回类型为null的时候,默认将其作为字符串处理;

 

如果三个假设成立,那么所有问题就得到解释。

 

那个朋友的SQL中,decode函数第一个可选返回值是null,Oracle识别返回类型为字符类型。之后对所有的其他返回值均使用了to_char方法类似的转换逻辑。

 

那么,往后想一步,如果Oracle decode函数真是依靠第一条件来确定列类型,其他列进行强行转换,那么如果出现不匹配的时候怎么办?

 

 

SQL> select decode(num,1,num,'k') from t;

 

select decode(num,1,num,'k') from t

 

ORA-01722: 无效数字

 

 

这个案例中的decode函数,根据第一个前条件取值num是数字类型,那么其他所有都会被强制转换为数字类型。但是我们写定的其他条件取值是’k’,不能进行强制类型转换。于是报错无效数字。

你可能感兴趣的:(Oracle,函数)