无效号码错误!似乎无法绕过它

发布于 2024-08-13 09:48:53 字数 699 浏览 6 评论 0原文

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。然后我使用解码语句设置一些要过滤的代码。 n=数字,c=字符。

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

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

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

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

Oracle 10g DB. I have a table called s_contact. This table has a field called person_uid. This person_uid field is a varchar2 but contains valid numbers for some rows and in-valid numbers for other rows. For instance, one row might have a person_uid of '2-lkjsdf' and another might be 1234567890.

I want to return just the rows with valid numbers in person_uid. The SQL I am trying is...

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

The translate replaces all numbers with spaces so that a trim will result in null if the field only contained numbers. Then I use a decode statement to set a little code to filter on. n=number, c=char.

This seems to work when I run just a preview, but I get an 'invalid number' error when I add a filter of...

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

I just don't understand what is happening! It should be filtering out all the records that are invalid numbers and 100 is obviously a number...

Any ideas anyone? Greatly Appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

决绝 2024-08-20 09:48:53

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

这给您留下了几个选择

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

Unfortunately, the various subquery approaches that have been proposed are not guaranteed to work. Oracle is allowed to push the predicate into the subquery and then evaluate the conditions in whatever order it deems appropriate. If it happens to evaluate the PERSON_UID condition before filtering out the non-numeric rows, you'll get an error. Jonathan Gennick has an excellent article Subquery Madness that discusses this issue in quite a bit of detail.

That leaves you with a few options

1) Rework the data model. It's generally not a good idea to store numbers in anything other than a NUMBER column. In addition to causing this sort of issue, it has a tendency to screw up the optimizer's cardinality estimates which leads to less than ideal query plans.

2) Change the condition to specify a string value rather than a number. If PERSON_UID is supposed to be a string, your filter condition could be PERSON_UID = '100'. That avoids the need to perform the implicit conversion.

3) Write a custom function that does the string to number conversion and ignores any errors and use that in your code, i.e.

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;

and then my_to_number(PERSION_UID) = 100

4) Use a subquery that prevents the predicate from being pushed. This can be done in a few different ways. I personally prefer throwing a ROWNUM into the subquery, i.e. building on OMG Ponies' solution

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 can't push the vp.person_uid = 100 predicate into the subquery here because doing so would change the results. You could also use hints to force the subquery to be materialized or to prevent predicate pushing.

2024-08-20 09:48:53

另一种选择是组合谓词:

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

Another alternative is to combine the predicates:

where case when translate(person_uid, '1234567890', ' ')) is null 
  then to_number(person_uid) end = 100
无声情话 2024-08-20 09:48:53

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

你能用“100”吗?

When you add those numbers to the WHERE clause it's still doing those checks. You can't guarantee the ordering within the WHERE clause. So, it still tries to compare 100 to '2-lkjsdf'.

Can you use '100'?

套路撩心 2024-08-20 09:48:53

另一种选择是应用子选择

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

Another option is to apply a subselect

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
天冷不及心凉 2024-08-20 09:48:53

正则表达式来救援!

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

Regular expressions to the rescue!

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

情徒 2024-08-20 09:48:53

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

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

Use the first part of your query to generate a temp table. Then query the temp table based on person_uid = 100 or whatever.

The problem is that oracle tries to convert each person_uid to an int as it gets to it due to the additional and statement in your where clause. This behavior may or may not show up in the preview depending on what records where picked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文