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

时间:2009-12-04 16:37:10

标签: 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'

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

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

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

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

任何想法?非常赞赏!

6 个答案:

答案 0 :(得分: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;

然后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 :(得分:4)

另一种方法是组合谓词:

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

答案 2 :(得分:3)

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

你能用'100'吗?

答案 3 :(得分:2)

另一种选择是应用子选择

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

答案 4 :(得分:0)

正常表达救援!

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

答案 5 :(得分:-1)

使用查询的第一部分生成临时表。然后根据person_uid = 100或其他任何内容查询临时表。

问题在于,由于where子句中的附加和声明,o​​racle尝试将每个person_uid转换为int。此行为可能会也可能不会显示在预览中,具体取决于选择的记录。