Oracle SQL PLSQL 大数字段异常行为

问题描述

一个名为 temptable 的现有表,列 largenumber 是一个 NUMBER 字段,没有设置精度:

largenumber NUMBER;

查询

select largenumber from temptable;

它返回:

-51524845525550100000000000000000000

但是如果我这样做

column largenumber format 999999999999999999999999999999999999999

然后

select largenumber from temptable;

它返回:

-51524845525550:100000000000000000000

为什么会有冒号?

为了测试,我取了数字,去掉冒号,插入到另一个表temptable2,做了同样的列largenumber格式,select返回的是没有冒号的数字:

select largenumber from temptable2;

它返回:

-51524845525550100000000000000000000

所以这里没有冒号。

那么原始数字字段中可能是什么导致冒号?

在原始行中,如果我执行选择并尝试执行任何 TO_CHAR、REPLACE、CAST 或连接到文本,则会出现数字转换错误

例如,尝试生成一个 csv:

select '"' || largenumber || '",'
FROM temptable;

会导致:

ora-01722 ("invalid number") error occurs when an attempt is made to convert a character string into a number,and the string cannot be converted into a valid number

解决方法

在评论中(为了回答我提出的问题),您分享了 dump(largenumber) 关于违规值返回的信息

Typ=2 Len=8: 45,50,56,53,52,48,46,48

从一开始,这意味着存储在磁盘上的数据是无效的(它不是 number 数据类型值的有效表示)。 Typ=2 是正确的,即数据类型 number。长度(8 个字节)是正确的(我们都可以数到 8 才能看到)。

错误的是字节本身。而且,我们只需要检查第一个和最后一个字节就可以看到。

第一个字节是 45。它对数字的符号和指数进行编码。第一位(1 或 0)表示符号:1 表示正,0 表示负。 45小于128,所以第一个字节的第一位为0;所以这个数字是负数。 (到目前为止,这符合您对预期值的了解。)

但是,对于负数,最后一个字节是总是幻值 102。总是。在您的原始问题下的另一条评论中,Connor McDonald 询问了您的平台 - 但这与平台无关,这是 Oracle 在任何平台上为永久存储编码数字的方式。因此,我们已经知道您获得的 dump 值告诉我们该值无效。

事实上,Connor 在同一个评论中给出了该数字的正确表示(根据 Oracle 的数字内部表示方案)。确实,只是最后一个字节是错误的:您的 dump 显示 48,但它应该是 102。

你如何解决这个问题?如果是一次性的,只需使用 update 语句将值替换为正确的值并继续。如果你的表有一个主键,我们称之为id,然后找到这一行的id,然后

update {your_table} set largenumber = -50...... where id = {that_id};

问题是,您的表中可能有多少这样的损坏值?如果它只是一个,你可以不理会它;但如果数量很多(甚至“少数”),您可能首先想弄清楚它们是如何到达那里的。

大多数情况下,数据库会拒绝无效值;例如,您不能简单地在 'abc' 列中插入 number。但是有多种方法可以获取错误数据;甚至是故意的,并且以可重复的方式。因此,您必须调查错误值是如何插入的(插入使用了什么过程)。

有关以可重复的方式在 number 列中插入错误数据的简单方法,您可以在 Oracle 开发人员论坛上查看此主题:https://community.oracle.com/tech/developers/discussion/3903746/detecting-invalid-values-in-the-db

请注意,我当时刚开始学习Oracle(不到两个月),所以我可能在那个帖子里说了一些愚蠢的话;但是插入错误数据的方法在那里有详细描述,并且已经过测试。这仅显示了在表中插入无效内容的一种可能(并且合理!)的方法;在您的具体案例中是如何发生的,您必须自行调查。