LightDB对decode函数功能增强

背景

decode函数是oracle数据库中一个功能强大且使用频率极高的函数,先来个decode函数的原型:

decode函数的使用方法是拿expr与search逐个比较,若满足expr与某个search相等,则函数返回该search对应的result;若expr与所有search值都不等,则返回default值,而当default参数未提供时,函数返回null。

注:在decode函数中,两个null比较是认为相等的。

旧版LightDB的oracle模式下,也支持decode函数,但是原先的实现逻辑是在比较前将expr、search统一转成字符串后再进行比较,这样就会出现如下案例中让人琢磨不透的结果:

lightdb@oracle=# select decode(1.0, 1, 2, 3); --转成字符串后,'1.0'和'1'确实不等了
 decode 
--------
      3
(1 row)

LightDB对decode的功能增强

在LightDB的23.4版本中,对decode函数进行了重构和功能增强,尽可能将其功能和行为主要修改点为:

1、当expr与searchn进行等值比较时,会先将expr和searchn(表示第2-n个search条件都要转换类型)转成search1的类型后再进行比较,若不能进行类型转换则直接报错。

lightdb@oracle=# select decode(1.0, 1, 2, 3);    --1.0需先转成int后再与1比较
 decode 
--------
      2
(1 row)

lightdb@oracle=# select decode('x',1,2,3);    --x无法转成int类型,所以报错
ERROR:  invalid input syntax for type numeric: "x"

2、resultn和default都是潜在的返回返回对象,但是在返回前需将类型转成result1的类型,然后再返回,若不能进行类型转换则直接报错。

--第3个参数的类型就是是decode函数的返回类型
lightdb@oracle=# select decode(3,1,'2','x');    --'x'转成字符后输出
 decode 
--------
 x
(1 row)

lightdb@oracle=# select decode(3,1,'2',3,'x','y');     --'x'转成字符后输出
 decode 
--------
 x
(1 row)

lightdb@oracle=# select decode(3,1,2,'x');    --x无法转成int类型则报错
ERROR:  invalid input syntax for type numeric: "x"

lightdb@oracle=# select decode(3,1,2,3,'x','y');    --x无法转成int类型则报错
ERROR:  invalid input syntax for type numeric: "x"

3、search、result和default可以是常量或复杂的表达式,LightDB会对result和default使用“短路”方式计算上述表达式的值,即每个search仅在与expr比较且比较结果为相等时才会计算result或default值,否则无需计算。但是这里有一个重要前提:必须存在源类型到目标类型转换规则,例如text类型的'x'转成numeric类型(decode处理时将所有字符类型转成text处理,数值类型当成numeric处理),pg_cast中存在转换规则,因此适用于该“短路”规则;而timestamp with time zone转成numeric类型,在pg_cast中不存在转换规则,此时不适用于该“短路"规则,而是直接报错了。另外该“短路”规则对search条件不适用。参考下节内容。

lightdb@oracle=# SELECT  
lightdb@oracle-#     DECODE(  
lightdb@oracle(#         12345,   
lightdb@oracle(#         012345.0,  
lightdb@oracle(#         1234567,    --直接返回了,后面的就跳过不计算了
lightdb@oracle(#         1234567890.1234567890,   
lightdb@oracle(#         9223372036854775807,  
lightdb@oracle(#         123.456,  
lightdb@oracle(#         123456789.9876543210,  
lightdb@oracle(#         'No Match'   --default值原本是无法转成int要报错的,存在text到numeric的类型转换,所以适用于短路规则,不报错
lightdb@oracle(#     ) AS decoded_value  
lightdb@oracle-# FROM  
lightdb@oracle-#     dual;
 decoded_value 
---------------
       1234567
(1 row)

lightdb@oracle=# select decode(3,
lightdb@oracle(# 1,1/0,    --expr与search不等,直接跳过result的计算
lightdb@oracle(# 2,2/0,    --expr与search不等,直接跳过result的计算
lightdb@oracle(# 3,4);
 decode 
--------
      4
(1 row)

lightdb@oracle=# SELECT DECODE(123, 
lightdb@oracle(#               1234, 123, 
lightdb@oracle(#               234, 'Not Match value',
lightdb@oracle(#               now()--不存在timestamp with time zone到numeric的转换规则,不适用于短路规则,报错
lightdb@oracle(#               ) FROM DUAL;
ERROR:  cannot cast type timestamp with time zone to numeric
LINE 4:               now()--不存在timestamp with time zone到numeric...
                   

已知的与Oracle的decode函数的差异

“短路”规则

请注意上述的“短路”操作仅针对result和default有效,对于search而言并不遵循此规则,这是PostgreSQL与Oracle内部实现差异所致,参考如下案例

lightdb@oracle=# SELECT DECODE(123, 
lightdb@oracle(#   123, 'Number Match', 
lightdb@oracle(#   'Not Match', 'Default') --'Not Match'需要转成search1的类型即int,无法转换则报错
lightdb@oracle-#   FROM DUAL;
ERROR:  invalid input syntax for type numeric: "Not Match"
LINE 3:   'Not Match', 'Default') --'Not Match'需要转成search1的类型...
          ^
lightdb@oracle=# 

字符类型转换

Oracle官网上,对于decode的字符比较是这么描述的:If expr and search are character data, then Oracle compares them using nonpadded comparison semantics.

对于这句话,本人的理解是将字符串中存在的padded character去掉后再比较,但好像实际执行结果并不是这样,参考如下实验

在LightDB中,目前遵循的规则是将expr和searchn转成search1的类型后再比较,目前LightDB基于的PG13.8版本中,char和varchar/text的类型转换规则是这样的:1-当char转varchar/text时,空格(padded character)会保留;2-当varchar/text转char时,会自动生成padded character。参考如下实验:

lightdb@oracle=# \d t1
                    Table "public.t1"
 Column |     Type      | Collation | Nullable | Default 
--------+---------------+-----------+----------+---------
 a      | character(10) |           |          | 
 b      | varchar2(10)  |           |          | 

lightdb@oracle=# select * from t1;
     a      | b 
------------+---
 a          | a
(1 row)

lightdb@oracle=# select a::varchar=b, b::char=a from t1;--保留padded char,所以不等
 ?column? | ?column? 
----------+----------
 f        | t
(1 row)

lightdb@oracle=# select a::varchar(10)=b, b::char(10)=a from t1;
 ?column? | ?column? 
----------+----------
 f        | t
(1 row)

lightdb@oracle=# select a::varchar(1)=b, b::char(10)=a from t1;
 ?column? | ?column? 
----------+----------
 t        | t
(1 row)

理解了PostgreSQL中字符串的类型转换后,如下decode函数产生的结果就能理解了:

lightdb@oracle=# \d t1
                    Table "public.t1"
 Column |     Type      | Collation | Nullable | Default 
--------+---------------+-----------+----------+---------
 a      | character(10) |           |          | 
 b      | varchar2(10)  |           |          | 

lightdb@oracle=# select * from t1;
     a      | b 
------------+---
 a          | a
(1 row)

lightdb@oracle=# select decode(a,b,1,2) from t1;    --char转varchar,保留padded char,所以a和b不等,所以返回2
 decode 
--------
      2
(1 row)

lightdb@oracle=# select decode(b,a,1,2) from t1;    --varchar转char,都保留padded char,所以a和b相等,所以返回1
 decode 
--------
      1
(1 row)

lightdb@oracle=# select decode(trim(a),b,1,2) from t1;    --对a先去掉padded char后,再转varchar,此时没有padded char了,a和b相等,所以返回1
 decode 
--------
      1
(1 row)

  • 31
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值