4

甲骨文 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 显然是一个数字......

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

4

6 回答 6

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谓词推送到此处的子查询中,因为这样做会改变结果。您还可以使用提示来强制实现子查询或防止谓词推送。

于 2009-12-04T18:04:05.260 回答
4

另一种选择是组合谓词:

where case when translate(person_uid, '1234567890', ' ')) is null 
  then to_number(person_uid) end = 100
于 2009-12-04T20:14:23.537 回答
3

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

你能用“100”吗?

于 2009-12-04T16:43:41.147 回答
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
于 2009-12-04T16:52:09.087 回答
0

正则表达式来拯救!

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

于 2011-01-06T21:29:36.597 回答
-1

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

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

于 2009-12-04T16:45:26.323 回答