【问题标题】:Invalid Number Error! Can't seem to get around it无效号码错误!似乎无法绕过它
【发布时间】:2010-12-23 07:39:12
【问题描述】:

Oracle 10g 数据库。我有一张名为s_contact 的表。该表有一个名为person_uid 的字段。这个person_uid 字段是一个varchar2,但包含一些行的有效数字和其他行的无效数字。例如,一行可能有一个 person_uid '2-lkjsdf' 而另一行可能是 1234567890。

我只想返回 person_uid 中具有有效数字的行。我正在尝试的 SQL 是...

select person_uid 
from s_contact 
where decode(trim(translate(person_uid, '1234567890', ' ')), null, 'n', 'c') = 'n'

翻译将所有数字替换为空格,因此如果字段仅包含数字,则修剪将导致 null。然后我使用 decode 语句设置一些代码进行过滤。 n=数字,c=字符。

这似乎在我只运行预览时有效,但当我添加过滤器时出现“无效号码”错误...

and person_uid = 100
-- or
and to_number(person_uid) = 100

我只是不明白发生了什么!它应该过滤掉所有无效数字的记录,100显然是一个数字......

有什么想法吗?非常感谢!

【问题讨论】:

    标签: sql oracle oracle10g ora-01722


    【解决方案1】:

    正则表达式助你一臂之力!

    where regexp_like (person_uid, '^[0-9]+$')

    【讨论】:

      【解决方案2】:

      另一种选择是组合谓词:

      where case when translate(person_uid, '1234567890', ' ')) is null 
        then to_number(person_uid) end = 100
      

      【讨论】:

        【解决方案3】:

        很遗憾,已提出的各种子查询方法不能保证有效。 Oracle 可以将谓词推送到子查询中,然后以它认为合适的任何顺序评估条件。如果在过滤掉非数字行之前碰巧评估了PERSON_UID 条件,你会得到一个错误。 Jonathan Gennick 有一篇出色的文章 Subquery Madness,非常详细地讨论了这个问题。

        这让你有几个选择

        1) 重新设计数据模型。将数字存储在除 NUMBER 列之外的任何内容中通常不是一个好主意。除了导致此类问题之外,它还倾向于搞砸优化器的基数估计,从而导致查询计划不理想。

        2) 更改条件以指定字符串值而不是数字。如果PERSON_UID 应该是一个字符串,那么您的过滤条件可能是PERSON_UID = '100'。这避免了执行隐式转换的需要。

        3) 编写一个自定义函数,将字符串转换为数字并忽略任何错误并在您的代码中使用它,即

        CREATE OR REPLACE FUNCTION my_to_number( p_arg IN VARCHAR2 )
          RETURN NUMBER
        IS
        BEGIN
          RETURN to_number( p_arg );
        EXCEPTION
          WHEN others THEN
            RETURN NULL;
        END;
        

        然后my_to_number(PERSION_UID) = 100

        4) 使用防止谓词被推送的子查询。这可以通过几种不同的方式来完成。我个人更喜欢将 ROWNUM 放入子查询中,即基于 OMG Ponies 的解决方案构建

        WITH valid_persons AS (
          SELECT TO_NUMBER(c.person_uid) 'person_uid',
                 ROWNUM rn
            FROM S_CONTACT c
           WHERE REGEXP_LIKE(c.personuid, '[[:digit:]]'))
        SELECT *
          FROM valid_persons vp
         WHERE vp.person_uid = 100
        

        Oracle 无法将vp.person_uid = 100 谓词推送到此处的子查询中,因为这样做会改变结果。您还可以使用提示来强制实现子查询或防止谓词推送。

        【讨论】:

        • 1) 改造模型不是一种选择。 2)我不能将被过滤的值更改为字符串,因为它实际上是一个外部系统进行过滤,它的 dtype 是一个数字。 3, 4) 我会试试 WITH 子句。如果这与视图不兼容(这实际上是该 SQL 将结束的位置),那么我将尝试自定义 to_number 函数。谢谢!我会告诉你的。
        • WITH 在这里并不特殊,使用正则表达式或使用翻译逻辑,您可以使用内联视图获得相同的结果。关键是您需要在内联视图中做一些事情来防止谓词被推入内联视图。这可以是一个提示,或者正如我所展示的,通过添加诸如 ROWNUM 之类的东西来防止谓词推送。
        【解决方案4】:

        另一个选项是应用子选择

        SELECT * FROM (
         select person_uid 
         from s_contact 
         where decode(trim(translate(person_uid, '1234567890', ' ')), null, 'n', 'c') = 'n'
        )
        WHERE TO_NUMBER(PERSON_UID) = 100
        

        【讨论】:

        • 不幸的是,这并不能保证首先执行内部子查询,并且在应用 PERSON_UID 过滤器之前只考虑包含所有数字数据的行
        • 对,我也有同样的想法,但我仍然收到“无效号码”错误。感谢您的尝试!
        【解决方案5】:

        使用查询的第一部分生成临时表。然后根据 person_uid = 100 查询临时表。

        问题在于,由于 where 子句中的附加 and 语句,oracle 尝试将每个 person_uid 转换为 int。此行为可能会或可能不会显示在预览中,具体取决于选取的记录。

        【讨论】:

        • 不幸的是,这个 SQL 是视图的一部分,所以我需要它作为一个语句。不过感谢您的想法!
        【解决方案6】:

        当您将这些数字添加到 WHERE 子句时,它仍在执行这些检查。您不能保证 WHERE 子句中的顺序。因此,它仍然尝试将 100 与 '2-lkjsdf' 进行比较。

        你能用'100'吗?

        【讨论】:

        • 外部系统实际上写入了将被过滤的值,并且该值始终是一个数字。因此,我需要在这个 SQL 中将字符串转换为一个数字,这将成为视图的一部分。不过谢谢!
        • 你是说100是外部系统提供的吗?然后只需将查询更改为: WHERE person_uid = CAST(@value_from_external_system AS VARCHAR)
        猜你喜欢
        • 1970-01-01
        • 2021-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-02
        • 2011-07-09
        • 1970-01-01
        相关资源
        最近更新 更多