【问题标题】:How to avoid Invalid Number on implicit conversion如何避免隐式转换时出现无效数字
【发布时间】:2018-01-13 08:19:52
【问题描述】:

我知道 Oracle(我正在研究 Oracle11gR2)如果可以的话,可以隐式地将数据类型相互转换。例如,如果我尝试将数字插入 varchar 区域,它会隐式地将数字转换为 varchar,反之亦然,如果 varchar 是有效数字,它将被转换为数字。

Oracle 也对连接进行这种隐式转换。如果我将数字列连接到 varchar 列,它将隐式将 varchar 转换为数字并完成查询。但如果 varchar 列中有任何无效数字,它会抛出 ORA-01722 : Invalid Number 您可以通过运行以下代码来查看此设置:

create table test_1(
  id varchar2(20),
  val number);

create table test_2(
  id number,
  name varchar2(20)
  );

insert into test_1 values ('abc', 10);
insert into test_1 values ('1', 11);
insert into test_2 values (1,'abc');
insert into test_2 values (2,'def');

-- Throw error
select 
  * 
from
 test_1, test_2
where
 test_1.id = test_2.id

-- work
select 
  test_1.id, val, name 
from
 test_1, test_2
where
 test_1.id = test_2.id
 and test_1.id = '1'

您还可以在以下位置查看和运行示例:http://sqlfiddle.com/#!4/fdce3/9/0

现在我的问题,是否有任何选项或配置参数可以强制 Oracle 将此隐式转换为 varchar 而不是数字?或者确切地看到无效数字的错误来源(哪个列或哪个连接)?

我知道我可以明确地进行转换以避免错误。如下所示,但我不希望将其作为解决方案。

select 
  * 
from
 test_1, test_2
where
 test_1.id = to_char(test_2.id)

您也可以访问http://sqlfiddle.com/#!4/fdce3/10 以查看上面的代码是否正常工作。

谢谢

【问题讨论】:

  • 我看不出这个问题有什么意义。即使您可以欺骗 Oracle 以您想要的方式进行隐式转换,它仍然会进行转换,因此从性能的角度来看,我不希望有任何差异。另外,不要使用隐式连接语法;它已经过时并且使您的查询难以阅读。
  • 正确的解决方案是:当你想存储数字时不要使用 VARCHAR2 数据类型!
  • @WernfriedDomscheit - 阿门!
  • @TimBiegeleisen - 这个问题有一个明显的观点:在执行时失败的查询会引发错误,但它没有说明它在哪个表的哪一列上失败。如果查询比较复杂,涉及很多表,很多列,可以用TO_CHAR()改变所有的条件,但是需要很长时间,需要大量的测试。 OP 想查找哪个表和列有无效数据,而不是更改查询。
  • 感谢 cmets! TimBiegeleisen “不要使用隐式连接”不幸的是,我们使用的 ETL 工具会自动创建这些连接,我无法控制它。我的问题不在于性能,而在于崩溃的代码,因为源系统将一些非数字值放入 ID 列。我知道,如果我们需要存储数字,那么列类型不应像 Wernfried Domscheit 所说的那样是 VARCHAR,但我无法控制源系统的数据模型。

标签: sql oracle


【解决方案1】:

" 可以显式进行转换以避免错误...我不希望将其作为解决方案"。

所以你不想使用好的做法?为什么不呢?虽然,如果您将数字列与字符串列进行比较,那匹马可能已经跑了。

随便吧。 ORA-01722 是一个数据库,它告诉我们一些有用的信息:它告诉我们'您期望test1.id 是数字,但您应该知道它包含非数字值'。现在我们可以解决这个错误了。

首先,这是对的吗?我们是否期望test1.id 包含非数字值?如果答案是“否”,那么我们有数据质量问题(更不用说数据建模问题),我们应该提出一个错误。

但是如果我们知道test1.id 可以合法地包含非数字字符串,那么我们必须相应地编写查询。这意味着我们需要在 WHERE 子句的另一边应用to_char()。这不仅可以处理错误,它还会向正在查看查询的未来同事挥动一个小标志:'顺便说一句,test1.id 包含非数字值:疯了,嗯?' p>

“是否有任何选项或配置参数可以强制 Oracle 将此隐式转换为 varchar 而不是数字?”

抑制异常总是一个坏主意。我们需要知道什么时候出了问题,这样我们才能正确地处理它。

“是否有任何选项或配置参数可以...准确查看无效数字的错误来源(哪个列或哪个连接)?”

唉,不。公平地说,期望您熟悉您正在使用的数据模型并不是没有道理的。但是,如果您不知道是哪些列导致了问题,那么除了查看数据字典(即all_tab_columns)之外,别无选择。

如果您想找出字符串列中的哪些行包含非数字值,则需要查询它。在 Oracle 12cR2 中有非常简洁的 VALIDATE_CONVERSION() 函数。 Find out more。如果您使用的是早期版本,则需要编写自己的版本,例如 this function in another StackOverflow thread


“我在 ETL 团队工作,我无法控制源数据模型。”

确实,ETL 的最大问题之一是处理来自源系统的不良数据。有多种方法,例如将数据加载到临时表中,对其进行验证,然后根据其质量将其发布到最终表或隔离表。或者,我们可以使用 DML 错误记录;这至少会隔离引发异常的记录。 Find out more.

【讨论】:

  • @mathguy - 是的,你是对的。那是一个适当的高级时刻:)
  • 感谢@APC,这是一个很好的答案。我的查询的问题是,我在一个 ETL 团队工作,我对源数据模型没有任何控制权。 (可能我会在星期一提出一个关于 DQ 的错误)他们将一些非数字数据放入 ID 列,这导致遗留代码(已有 10 年历史,有 50 多个列,20 个源表)崩溃。由于它是一个古老而复杂的查询,我不想修改它。正如我已经说过的,我们正在使用 11gR2,但计划升级 12cR2,然后我将尝试使用 VALIDATE_CONVERSION() 函数。再次非常感谢。
【解决方案2】:

您不需要任何特殊技巧,只需使用“仅数字”的正则表达式匹配“数字”的那些 id:

select 
  test_1.id, val, name 
from
 test_1, test_2
where
 test_1.id = test_2.id
 and regexp_like (test_1.id, '^[0-9]+$')

regexp_like documentation

【讨论】:

  • @Ben 已修复。 (我比较习惯mysql)
  • 其实我们需要使用JOIN..ON语法
  • 这是一个糟糕的测试。它将拒绝小数和负数作为非数字。显然你可以将它们添加到你的模式中,但为什么要麻烦呢?在 Oracle 中,有更好(更具性能)的方法来测试字符串是否可以转换为数字,我在回复中谈到了这一点。
  • @KaushikNayak 不,我们不需要join(尽管这是一个好主意),因为 OP 的查询是内连接。我对 OP 的查询做了最少的工作以使其正常工作。改进代码风格无助于回答实际问题。
  • @APC 这是一个很棒的测试。 id 值将是非负整数。如果在一些奇怪的宇宙中,id 既可以是正面的也可以是负面的,它只需要将正则表达式调整为 '^-?[0-9]+(\.[0-9]*)?'... 很重要。
猜你喜欢
  • 1970-01-01
  • 2013-08-15
  • 2015-08-08
  • 1970-01-01
  • 1970-01-01
  • 2014-10-09
  • 1970-01-01
  • 2020-02-12
  • 1970-01-01
相关资源
最近更新 更多