简介: 怎么对齐两段union脚本的数据类型
第1章 问题概述
1.1 UNION中隐式类型转换问题
近期参加的一个公有云我的项目要降级,因为maxcompute要降级到更新的版本,对之前的一些SQL写法有个更高的要求,就引出了这个union隐式转换的问题。运维同学扫描到外部的异样是:union.string.meet.non.string。
在ODPS某些模式中在union两侧对应列如果类型不同时会尝试隐式类型转换,其行为是一边为string,另一边为数字或datetime类型时,转为另一边的类型(string)。然而绝大多数的数据库或者开源生态而言,应用的都不是这种转换规则,比方hive,mysql等会优先转成string。这种不确定的转换规则有时候会很危险,如用户从hive往odps迁徙时,可能会导致无声无息的精度损失,语义谬误等。 ODPS2.0为了平安禁止此隐式类型转换(这也是目前oracle的默认行为),如果须要请应用CAST函数。(之前好好的,当初要报错了)所以当初项目组要求脚本作者查看本人脚本,明确要转到的类型,如果须要退出显式转换。
例:select * from (--(谬误)

select a_bigint c1 from t1

union all

select a_string c1 from t2) x;

-- 如果心愿后果c1为bigint类型(这是目前ODPS的行为),改为

select * from (--(正确)

select a_bigint c1 from t1

union all

select cast(a_string as bigint) c1 from t2) x;

-- 如果心愿后果c1为string类型(这是目前HIVE的行为),改为

select * from (--(正确)

select cast(a_bigint as string) c1 from t1

union all

select a_string c1 from t2) x;

1.2 问题剖析
因为还未降级,目前脚本也不会报错,maxcompute的异样咱们也捕捉不到,革新的压力有点纯靠肉眼辨认了,着实有点惆怅。

谬误示例:

select 123 as aa,0 as ab

from xlog

union ALL

select getdate() as aa,0 as ab

from xlog;

FAILED: ODPS-0130241:[4,8] Illegalunion operation - type mismatch for column 0 of UNION, left is BIGINT while right is DATETIME

--正文:这里的[4,8]是指第四行,第八个字符开始也就是getdate().

那怎么去疾速的定位到是哪个字段呢?我翻了一下后盾检索进去的上百个脚本,脚本代码在500-1000行之间居多,union 的数量在单个脚本中少则三五个,多的有二十几个。呆了一早上,毫无停顿。

第2章 问题解决
简略的思考了一下,要想取得Union的两个表数据类型是否对齐,就得看下原来表构造中的数据类型,指标表构造的数据类型,还需看一下代码找到SQL逻辑执行后的数据类型,这样能力找到哪些字段数据类型不统一。

于是依照这个思路开始看,第一个脚本的代码就1000多行,union的表字段数量也是100多个,union还有6个。间接懵了,齐全肉眼无奈辨认。一早上就这么过来了,岂但一个没有搞定,还把本人搞烦了。

2.1 利用执行打算
一抽莫展之际,忽然想到了执行打算。MaxCompute的执行打算,尽管会不会刚好会展现输入的数据类型呢?答案:会的。

explain

select 123 as aa,0 as ab

from xlog

;

Job Queueing...

job0 is root job

In Job job0:

root Tasks: M1

In Task M1:

Data source: mujiao.xlog

TS: mujiao.xlog

   SEL: 123L aa, 0L ab       FS: output: Screen           schema:             aa (bigint)             ab (bigint)

OK

explain

select getdate() as aa,0 as ab

from xlog;

;

Job Queueing...

job0 is root job

In Job job0:

root Tasks: M1

In Task M1:

Data source: mujiao.xlog

TS: mujiao.xlog

   SEL: 1655965081824 aa, 0L ab       FS: output: Screen           schema:             aa (datetime)             ab (bigint)

OK

咱们看到在FS:output:Screen 上面是schema:aa(bigint),ab(bigint)。这就是咱们能够利用的数据类型了。所以,咱们能够把长脚本中的union一段一段的explain,而后截取这部分内容,比拟多个schema的不同。

schema1: schema2:

aa (bigint) aa (datetime)

ab (bigint) ab (bigint)

这样就肉眼可视的发现其实union中两段SQL的字段aa是不同的。

2.2 其余问题
其余相干的一些问题:

1) 执行打算中的max_pt()函数无奈在开发环境应用,因为开发环境没有分区,这个函数会间接报错。要么删除、正文这个函数,要么在表后面减少生产环境前缀。

2) 超长的SQL段,执行打算可能有几百行上千行,找不到最终的output。能够在日志中搜寻“output: Screen”这段对应的就是最终的输入。

3) 太多的字段,肉眼无奈判断哪些类型不一样的时候,倡议在excel中来比拟,利用excel的筛选能力,一一数据类型筛选比拟。

4) 执行打算在特地的状况下可能出不来,应用create table as创立一个长期表来辨认SQL输入的数据类型,而后再desc表构造。不过每个字段都要给一个名称,在create table的时候,还有null这种写法也是须要cast后给一个明确的数据类型。

5) 日期转换,因为string到日期转换的格式化类型不是能猜出来的,倡议理论看一下数据格式,不要猜想。否则只能线上运行后,报错能力排查出问题。

6) 对于Null值,能够cast(null as datetime)、cast(null as double)给字段赋值。

即使这些都能够,对于数百个长达几百行的脚本来说,这项工作都足以让你烦躁不安失去急躁。倡议研发同学还是劳逸结合,再就是日后把这个工作变成一个习惯。一大段SQL的union,就间接先explain,别等报错一个一个看心烦。

最初,你会发现这所有的原因还是咱们的根底工作没有做好。既然是union一起的数据字段,实践数据类型和值域是截然不同的,怎么会出这种问题。标准化的数据应该是日期就是日期,数值就是数值,字符就是字符,不会数值存储成字符、日期存储成字符。显然,当初的苦楚还是来源于之前的工作缺失,做好每一步,前面会越来越轻松。

2.3 另外一个办法
起初跟研发同学要到了一个能够让warning信息显示进去的提醒。

setodps.compiler.warning.disable=false;

sql running .....

WARNING:[4,8] implicit conversion from bigint to datetime,use cast function to suppress

这个warning会让所有的隐式转换都抛出来,在现场环境中,显著比我理论依照explain的办法判断进去的要多很多。这两种办法,在理论应用中该如何应用,大家能够自行判断。

祝大家好运!

原文链接:http://click.aliyun.com/m/100...
本文为阿里云原创内容,未经容许不得转载。