内容纲要
概要描述
Inceptor 中 DECIMAL(p,s) 有两个参数:
- p 为类型的精度(precision):它规定了该 DECIMAL 类型总共可以有多少位(包括小数点前和小数点后的位)。Inceptor中精度最大不能超过38。
- s 为类型的标度(scale):它规定了该 DECIMAL 类型小数点后面的位数。s 不能大于 p。(decimal列的s不支持负数, 但是可以通过round传入负scale来达到类似Oracle的效果,不过Inceptor中会认为结果是scale=0的一个数)
- 如果数据无法按照指定的精度和标度表示,将被认为是 NULL值。
详细说明
1. 新建一张ORC普通表EMP_ORC
> create table test(v1 decimal(38,20), v2 decimal(38,20));
> insert into test select 1,2 from system.dual;
--执行结果为null,与实际不符
> select v1*v2 from test; --null
2. 原理分析
两个decimal(p,s)数据类型相乘,Inceptor中首先根据输入的decimal(常量表达式根据实际数据的ps,其他根据列的ps)的Precision和Scale计算目标Precision和Scale。
操作 | 结果Precision | 结果Scale | |
---|---|---|---|
e1*e2 | p1+p2+1 | s1+s2 | * |
对于乘法运算,如果算出来的Precision和Scale超过38,只取到38. 即:
- 如果Precision 超过38,Scale没有超过38,则目标类型为decimal(38,scale)
- 如果Precision和Scale都超过38,则目标类型为decimal(38,38)
*以下面的例子为例,两个decimal(38,20)数据类型相乘,结果Precision=38+38+1=77,结果Scale=20+20=40,Precision和Scale都超过38,Inceptor会优先保证Scale,所以实际推断值为decimal(38,38),而12=2,无法存入decimal(38,38),认为超界报错,所以为null。**
解决方案
方案一:TDH6.0.2版本可以通过参数,在数据超界时报错通知
> set inceptor.strict.evaluate=true; --默认是false,这是之前已经存在的开关
> set inceptor.decimal.null.check=true; --默认是false,这个是控制Decimal报错的关键开关,但是需要上面的开关配合,否则报错会被吞掉
方案二:通过Floating scale来尽量保存结果的整数部分
短时间内我们只能用Floating scale来workaround,Floating Scale会尽量保存结果的整数部分,该参数默认为false,需要手动set。后续已安排开发计划(Support decimal without precision/scale like number type in oracle)
> set inceptor.floating.scale.decimal=true;
FAQ
inceptor.floating.scale.decimal
该参数开关打开之后 create table xx as select
语句和 with xx as
语句可能会不支持,报错 floating scale decimal is not supported
, 解决方案是 要么在 select
语句里的对应列外面套上 cast(xx as decimal(p,s))
. 或者直接单独语句建表,把对应列的定义显示的写出来 。 下面举个例子:
--问题复现:
drop table IF EXISTS t2;
create table t2(a decimal(38,25));
insert into t2 select 312.0217 from system.dual;
select a*0.476893*0.476893*0.476893 from t2; --NULL
SET inceptor.floating.scale.decimal =TRUE;
-- 下面的语句执行报错:java.sql.SQLException: COMPILE FAILED: Semantic error: [Error 11371] floating scale decimal is not supported. Column name: _c0, column type: decimal(48,-2147483648)
CREATE TABLE test0002 AS select a*0.476893*0.476893*0.476893 from t2;
--解决方案:
--方案一:
CREATE TABLE test0002 AS select CAST (a*0.476893*0.476893*0.476893 AS decimal(38,20))from t2;
SELECT * FROM test0002;
--方案二:
DROP TABLE IF EXISTS test0003;
CREATE TABLE test0003 (DT DECIMAL(38,20));
INSERT INTO test0003 select a*0.476893*0.476893*0.476893 from t2;
SELECT * FROM test0003;