dws的除法
有客户提出疑问,在dws里执行select 1.0/2,时结果为0.50000000000000000000,结果中的后面0太多,对业务有影响。
下面解析下出现该情况的原因。
首先我们建立个测试用例
create table divtest (data_int int,data_float float8,data_numeric numeric);
insert into divtest values(1,1,1);
postgres=# select data_int/2,data_float/2,data_numeric/2 from divtest;
?column? | ?column? | ?column?
----------+----------+------------------------
0 | 0.5 | 0.50000000000000000000
从上面的现象可以看出,这里是把select 1.0/2看做是numeric的除法了。
这里参考下postgres的官方文档描述:如果一个不包含小数点和指数的数字常量的值适合类型integer(32 位),它首先被假定为类型integer。否则如果它的值适合类型bigint(64 位),它被假定为类型bigint。再否则它会被取做类型numeric。包含小数点和/或指数的常量总是首先被假定为类型numeric。【1】
查询pg_operator表,也可以用\do查询
postgres=# \doS+ /
List of operators
Schema | Name | Left arg type | Right arg type | Result type | Function | Description
------------+------+------------------+------------------+------------------+---------------+------------------------------
pg_catalog | / | bigint | bigint | bigint | int8div | divide
pg_catalog | / | bigint | integer | bigint | int84div | divide
pg_catalog | / | bigint | smallint | bigint | int82div | divide
pg_catalog | / | box | point | box | box_div | divide box by point (scale)
pg_catalog | / | circle | point | circle | circle_div_pt | divide
pg_catalog | / | double precision | double precision | double precision | float8div | divide
pg_catalog | / | double precision | real | double precision | float84div | divide
pg_catalog | / | integer | bigint | bigint | int48div | divide
pg_catalog | / | integer | integer | integer | int4div | divide
pg_catalog | / | integer | smallint | integer | int42div | divide
pg_catalog | / | interval | double precision | interval | interval_div | divide
pg_catalog | / | money | bigint | money | cash_div_int8 | divide
pg_catalog | / | money | double precision | money | cash_div_flt8 | divide
pg_catalog | / | money | integer | money | cash_div_int4 | divide
pg_catalog | / | money | money | double precision | cash_div_cash | divide
pg_catalog | / | money | real | money | cash_div_flt4 | divide
pg_catalog | / | money | smallint | money | cash_div_int2 | divide
pg_catalog | / | numeric | numeric | numeric | numeric_div | divide
pg_catalog | / | path | point | path | path_div_pt | divide (rotate/scale path)
pg_catalog | / | point | point | point | point_div | divide points (scale/rotate)
pg_catalog | / | real | double precision | double precision | float48div | divide
pg_catalog | / | real | real | real | float4div | divide
pg_catalog | / | smallint | bigint | bigint | int28div | divide
pg_catalog | / | smallint | integer | integer | int24div | divide
pg_catalog | / | smallint | smallint | smallint | int2div | divide
(25 rows)
在操作符匹配中,会根据以上规则匹配,若匹配不上,则会进行类型转换再匹配,若仍无法匹配则会报错。
综合上述
select 1/2 当1为int时,smallint/smallint,使用int2div,返回smalint,整数除法结果0
这里跳过float除法在后面解释
select 1/2 当1为numeric时,numeric/numeric,numeric_div,返回numeric除法结果0.50000000000000000000。
select 1.0/2会看成为select 1.0::numeric/2::numeric,再通过使用的/的函数是numeric_div,
可以查询pg_proc表,也可以使用\sf查询定义
postgres=# \sf+ numeric_div
CREATE OR REPLACE FUNCTION pg_catalog.numeric_div(numeric, numeric)
RETURNS numeric
LANGUAGE internal
IMMUTABLE PARALLEL SAFE STRICT
1 AS $function$numeric_div$function$
结果会返回类型为numeric的,该值为精确值,并保留了一定的数字长度。
用下面示例可以确认numeric_div返回精确值,而float8div浮点数非精确值。
postgres=# select data_int/6,data_float/6,data_numeric/6 from divtest;
?column? | ?column? | ?column?
----------+---------------------+------------------------
0 | 0.16666666666666666 | 0.16666666666666666667
postgres=# select data_int/6,data_float/6=0.16666666666666666,data_numeric/6=0.16666666666666666667 from divtest;
?column? | ?column? | ?column?
----------+----------+----------
0 | t | t
(1 row)
postgres=# select data_int/6,data_float/6=0.16666666666666667,data_numeric/6=0.16666666666666666666 from divtest;
?column? | ?column? | ?column?
----------+----------+----------
0 | t | f
(1 row)
可以看到numeric_div的返回值只能和0.16666666666666666667相等,是精确的,而浮点数和2个值都相等。
让我们再回到浮点数除法。
select 1/2 当1为float8时,double precision/double precision即float8/float8,使用float8div,返回double precision,浮点数除法结果默认15位有效位,即0.500000000000000。
又由于浮点数显示受extra_float_digits控制,在默认值0的情况下,浮点值以其最短精确的十进制表示的文本形式输出0.5。
注;dws基于pg9.2,extra_float_digits在pg不同版本有些许差别,这里不展开,具体差别可以查询pg相关文档。
最后,回到客户疑问,我们可以通过指定数据类型或者类型转换来解决
postgres=# select 1.0::float/2;
float8
--------
0.5
(1 row)
注【1】:http://postgres.cn/docs/12/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS
- 点赞
- 收藏
- 关注作者
评论(0)