数字错误无效!似乎无法绕过它

Nat*_*ate 4 sql oracle oracle10g ora-01722

Oracle 10g DB.我有一张叫做的桌子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'
Run Code Online (Sandbox Code Playgroud)

translate使用空格替换所有数字,以便如果字段仅包含数字,则trim将导致null.然后我使用decode语句设置一个小代码来过滤.n =数字,c = char.

当我只运行预览时,这似乎有效,但当我添加过滤器时,我收到"无效数字"错误...

and person_uid = 100
-- or
and to_number(person_uid) = 100
Run Code Online (Sandbox Code Playgroud)

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

任何人的想法?非常感激!

Jus*_*ave 6

不幸的是,已经提出的各种子查询方法并不能保证有效.允许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;
Run Code Online (Sandbox Code Playgroud)

然后 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
Run Code Online (Sandbox Code Playgroud)

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