GaussDB(DWS)函数不同写法引发的结果差异
本文分享自华为云社区《GaussDB(DWS)函数结果差异案例之greatest》,作者: 你是猴子请来的救兵吗。
GaussDB(DWS)支持多种兼容模式,为了兼容目标数据库,各模式之间或多或少存在一些行为差异。这里分享一个mysql兼容模式下的表达式函数因不同写法引发的结果差异案例。
问题背景
问题版本 GaussDB 8.1.1
问题描述
用户反馈mysql兼容模式下,以下两条sql的执行结果存在差异: select greatest(1,2,100,-1,0,nvl(null,0)) 出来的结果是 2 select greatest(1,2,100,-1,0) 出来结果是 100
场景再现
mysql=# select greatest(1,2,100,-1,nvl(null,0)); greatest ---------- 2 (1 row) mysql=# select greatest(1,2,100,-1,0,0); greatest ---------- 100 (1 row)
根因分析
1,不知道小伙伴们有没有注意到,这两个结果集的显示一个是靠左的一个是靠右的;ok,我们先来确认下这两个结果的数据类型:
mysql=# select pg_typeof(greatest(1,2,100,-1,nvl(null,0))); pg_typeof ----------- text (1 row) mysql=# select pg_typeof(greatest(1,2,100,-1,0)); pg_typeof ----------- integer (1 row)
2,依靠pg_typeof我们拿到了返回结果的数据类型;这就说明第一条语句是以text类型进行排序选择最大值的,依次为(‘0’,‘1’,’-1’,‘100’,‘2’),因此我们得到最大值是字符串类型的’2’。
0 1 -1 100 2
3,依次类推,第二条语句是以int类型进行排序选择最大值的,依次为(-1,0,1,2,100),因此我们得到最大值是数值类型的100。
-1 0 1 2 100
4,表达式函数greatest的返回类型是基于入参类型确定的,这里的差异是由于第五个入参类型导致的结果差异。
mysql=# select pg_typeof(nvl(null,0)); pg_typeof ----------- text (1 row) mysql=# select pg_typeof(0); pg_typeof ----------- integer (1 row)
5,而nvl/greatest之所以会出现不同的返回类型,是由mysql兼容模式下的类型匹配规则决定的。
具体规则可参考:UNION,CASE和相关构造。
修改建议
针对此差异场景,建议在不确定返回类型时显式指定其入参类型,将nvl(null,0)改为nvl(null,0)::int,这样结果就是已int排序的,与另一台语句预期相符。
mysql=# select greatest(1,2,100,-1,nvl(null,0)::int); greatest ---------- 100 (1 row)
知识剖析
SQL UNION构造把不相同的数据类型进行匹配输出为统一的数据类型结果集。因为SELECT UNION语句中的所有查询结果必须在一列里显示出来,所以每个SELECT子句中的元素类型必须相互匹配并转换成一个统一的数据类型。同样的要求广泛存在于 UNION、ARRAY 和 CASE、COALESCE、IF、IFNULL 和 GREATEST、LEAST 和 NVL 等表达式和函数中。
GaussDB(DWS)支持多种兼容模式,不同兼容模式下的类型匹配规则也不尽相同。为了便于理解,这里仅以mysql兼容模式下 IFNULL 的类型匹配规则进行举例说明,它与 GREATEST 在mysql兼容模式下的规则是一致的。
规则1: 如果所有输入都是相同的类型,不包括unknown类型,那么解析成所输入的相同数据类型。
mysql=# select pg_typeof(1),pg_typeof(2); pg_typeof | pg_typeof -----------+----------- integer | integer (1 row) mysql=# select ifnull(1,2),pg_typeof(ifnull(1,2)); ifnull | pg_typeof --------+----------- 1 | integer (1 row)
规则2: 如果所有输入都是unknown类型则解析成text类型。(常量字符串就是unknow类型)
mysql=# select pg_typeof('1'),pg_typeof('2'); pg_typeof | pg_typeof -----------+----------- unknown | unknown (1 row) mysql=# select ifnull('1','2'),pg_typeof(ifnull('1','2')); ifnull | pg_typeof --------+----------- 1 | text (1 row)
规则3: 如果输入是unknown类型和某一非unknown类型,则解析成该非unknown类型。
mysql=# select pg_typeof(current_date),pg_typeof('20230801'); pg_typeof | pg_typeof -----------+----------- date | unknown (1 row) mysql=# select ifnull(current_date,'20230801'),pg_typeof(ifnull(current_date,'20230801')); ifnull | pg_typeof ------------+----------- 2023-08-10 | date (1 row)
规则4: 如果存在多种非unknown类型,将enum类型当做text类型,再进行比较。
mysql=# create type gender as enum('boy','girl'); CREATE TYPE mysql=# select pg_typeof('boy'::gender),pg_typeof('girl'::varchar); pg_typeof | pg_typeof -----------+------------------- gender | character varying (1 row) mysql=# select ifnull('boy'::gender,'girl'::varchar),pg_typeof(ifnull('boy'::gender,'girl'::varchar)); ifnull | pg_typeof --------+----------- boy | text (1 row)
规则5: 如果输入类型是同一个类型范畴,则选择该类型的优先级较高的类型。如果是不同的类型范畴,则解析成text类型。
--相同类型范畴 mysql=# select pg_typeof(1),pg_typeof(2.0); pg_typeof | pg_typeof -----------+----------- integer | numeric (1 row) mysql=# select ifnull(1,2.0),pg_typeof(ifnull(1,2.0)); ifnull | pg_typeof --------+----------- 1 | numeric (1 row) --不同类型范畴 mysql=# select pg_typeof(1),pg_typeof(current_date); pg_typeof | pg_typeof -----------+----------- integer | date (1 row) mysql=# select ifnull(1,current_date),pg_typeof(ifnull(1,current_date)); ifnull | pg_typeof --------+----------- 1 | text (1 row)
规则6: 把所有输入转换为所选的类型。如果从给定的输入到所选的类型没有隐式转换则失败。
--json不存在到text的隐式转换 mysql=# select pg_typeof(1),pg_typeof('{"a":1}'::json); pg_typeof | pg_typeof -----------+----------- integer | json (1 row) mysql=# select ifnull(1,'{"a":1}'::json),pg_typeof(ifnull(1,'{"a":1}'::json)); ERROR: IFNULL could not convert type json to text LINE 1: select ifnull(1,'{"a":1}'::json),pg_typeof(ifnull(1,'{"a":1}... ^ CONTEXT: referenced column: ifnull --可以尝试显式指定类型转换 mysql=# select ifnull(1,'{"a":1}'::json::text); ifnull -------- 1 (1 row)

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Vim 项目的未来
Vim 作者 Bram Moolenaar 一周前去世,他被称为是 Vim 项目的终身仁慈独裁者,管理着 Vim 项目的各个方面。他的去世留下了巨大的空白。 在 vim_dev 邮件列表上,Vim 核心开发者 Christian Brabandt 宣布了项目的未来计划,称从 Bram 家人获得了 Vim 在 Github 上的管理权,邀请了更多开发者加入了 Vim 组织,已经开始合并 commits,但目前主要是 bug 修正、安全补丁和文档更新等。 其他的计划还包括: 处理当前积压的工作后,发布 Vim 9.1 未来让 Vim 变得更现代(类似 Neovim 的做法) 接管 Vim 所有邮件列表的管理和访问权限 暂时无法访问 Vim FTP 主服务器,正在接触 Bram 的家人是否知道凭证 迁移 Vim 项目主页 讨论是否将 Vim 项目主页开源 详情。
- 下一篇
网易互娱出海之旅:大数据平台上云架构设计与实践
2020 年初,随着网易互娱的海外业务增长与海外数据合规的需求,我们开始了网易互娱大数据离线计算平台迁移出海的工作。前期,我们采取了云主机裸机加上高性能 EBS 块存储的方案。但是,这个方案存储费用高昂,成本是国内自建机房的数十倍。 于是,我们决定在公有云上构建一个平台,这个平台不仅需要更加适应当前业务场景、与历史业务更为兼容,还要比公有云的 EMR 托管方案更为经济。我们主要从存储、计算和数据分层生命周期管理三方面进行了成本优化,具体的优化方案将在下文为大家详细介绍。 最终,这个项目给下游数据业务和分析部门提供了完整 Hadoop 的兼容性,避免了所有业务逻辑推倒重来;给游戏数据业务出海节省了大量成本,存储成本为优化前的 50%,算力总成本为优化前的 40%,冷数据成本为优化后线上存储成本的 33%。未来随着业务量的增加,成本节约按 10 倍比例节约相应的费用,为出海后的数据化运营等提供有力支持。 01. 大数据平台海外上云方案设计 在 2020 年,我们开始了一项紧急的出海任务。在国内,我们的业务一直以自建集群的方式进行部署和运行。为了在海外能够快速上线,我们紧急上线了一个与国内集...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6