我们的 Oracle 数据源之一拥有数百个表,其中所有数字列都使用 NUMBER 数据类型定义,没有精度和小数位数。但事实上,列可以存储纯整数值或小数值 - 无法仅通过查看数据类型来判断。现在,这是一个大问题,因为当我尝试将任何这些数据加载到大数据工具(Sqoop、Hive、Spark 等)时,所有这些工具都将这些列视为字符串,这是一个处理起来非常痛苦的问题。我认为这是所有基于 JDBC/Java 的工具的问题。
是否可以以某种方式检测存储在 NUMBER 类型列中的值的实际精度和小数位数。我希望 Oracle 将其保留在元数据表中的某个位置,但我在任何地方都没有看到它。我的最后一招是对表中的数据进行随机采样,并在一侧存储转换模式,但我希望有更好的方法。我真的很喜欢
例如,表
TEST
有三列:ID
、AMOUNT
和 QUANTITY
,全部声明为 NUMBER,未指定精度或小数位数。
但事实上
ID
应该是bigint,AMOUNT
应该是decimal(18,6),QUANTITY
应该是int。
create table test (
ID number,
AMOUNT number,
QTY number
)
ID AMOUNT QTY
1 200.56 4
2 23.754 5
我不可能手动进行映射,因为我有 600 个表,每个表有 50-300 列。数据采样是我最后的手段。
一种选择是查询存在的值。
请务必将下面示例中的
{{.THE_COLUMN}}
和 {{.THE_TABLE}}
替换为您的列和表名称。根据您用于提交查询的环境,您可能需要附加一行由单斜杠 (/
) 组成的行。
请注意,如果列中没有非空数据,
NUMBER_PRECISION
和 NUMBER_SCALE
将为空。否则,请注意它们可能会超出目标平台的数字限制。
with
function WHOLE_DIGITS( NBR number, SEP varchar2 )
return number
is
STR varchar2(40) := to_char( abs( NBR ) );
POS number := instr( STR, SEP );
begin
if POS = 0 then
return length( STR );
end if;
return POS - 1;
end WHOLE_DIGITS;
function FRACT_DIGITS( NBR number, SEP varchar2 )
return number
is
STR varchar2(40) := to_char( NBR );
POS number := instr( STR, SEP );
begin
if POS = 0 then
return 0;
end if;
return length( STR ) - POS;
end FRACT_DIGITS;
select rr.MAX_WHOLE_DIGITS + rr.MAX_FRACT_DIGITS
as NUMBER_PRECISION
, rr.MAX_FRACT_DIGITS
as NUMBER_SCALE
from (
select max( WHOLE_DIGITS( ss.NBR, nn.SEP ) )
as MAX_WHOLE_DIGITS
, max( FRACT_DIGITS( ss.NBR, nn.SEP ) )
as MAX_FRACT_DIGITS
from (
select tt.{{.THE_COLUMN}}
as NBR
from {{.THE_TABLE}}
tt
)
ss
cross
join (
select substr( tt.VALUE, 1, 1 )
as SEP
from NLS_SESSION_PARAMETERS
tt
where tt.PARAMETER = 'NLS_NUMERIC_CHARACTERS'
)
nn
where ss.NBR is not null
)
rr
Oracle 数字类型默认为 38.0
要获取创建时定义到
SCALE
列的 PRECISION
和 NUMBER
,您可以查询 Oracle 的数据字典视图 ALL_TAB_COLUMNS
,或者 USER_TAB_COLUMNS
(如果所有表都在同一架构下)。
这些视图将信息保存在
DATA_PRECISION
和 DATA_SCALE
列中。