无效号码错误!似乎无法绕过它
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不幸的是,所提出的各种子查询方法并不能保证有效。 Oracle 可以将谓词推入子查询中,然后以它认为合适的任何顺序评估条件。如果在过滤掉非数字行之前碰巧评估
PERSON_UID
条件,您将收到错误。 Jonathan Gennick 有一篇出色的文章子查询疯狂,非常详细地讨论了这个问题。这给您留下了几个选择
: 1) 重新设计数据模型。通常,将数字存储在 NUMBER 列以外的任何内容中都不是一个好主意。除了导致此类问题之外,它还可能会破坏优化器的基数估计,从而导致查询计划不太理想。
2) 更改条件以指定字符串值而不是数字。如果
PERSON_UID
应该是字符串,则您的过滤条件可以是PERSON_UID = '100'
。这避免了执行隐式转换的需要。3) 编写一个自定义函数,执行字符串到数字的转换并忽略任何错误,然后在代码中使用该函数,即
然后
my_to_number(PERSION_UID) = 100
4) 使用子查询来防止谓词被推。这可以通过几种不同的方式来完成。我个人更喜欢将 ROWNUM 放入子查询中,即基于 OMG Ponies 的解决方案构建,
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 bePERSON_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.
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
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.另一种选择是组合谓词:
Another alternative is to combine the predicates:
当您将这些数字添加到 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'?
另一种选择是应用子选择
Another option is to apply a subselect
正则表达式来救援!
其中 regexp_like (person_uid, '^[0-9]+$')
Regular expressions to the rescue!
where regexp_like (person_uid, '^[0-9]+$')
使用查询的第一部分生成临时表。然后根据 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.