Oracle 中的 TO_NUMBER 函数出现奇怪的问题

发布于 2024-08-31 19:01:00 字数 1435 浏览 6 评论 0原文

如果记录数超过特定数量 n,则在 varchar2 列的 where 子句中执行 to_number 函数时,我遇到间歇性问题。我使用 n 是因为没有发生这种情况的确切记录数。在一个 DB 上,它发生在 n 为 100 万之后,而在另一个 DB 上,n 为 0.1。百万。

例如,我有一个包含 1000 万条记录的表,其中包含包含数字数据和 Id 的 field1 varchar2

如果我执行查询作为示例,

select * 
from country 
where to_number(field1) = 23
and id >1 and id < 100000

这可以工作

但是如果我执行查询,

select * 
from country 
where to_number(field1) = 23 
and id >1 and id < 100001

它会失败,并显示无效数字

接下来我尝试查询,

select * 
from country
where to_number(field1) = 23 
and id >2 and id < 100001

它会再次工作

由于我只得到无效的数字,这很令人困惑,但在日志文件中它说它

Memory Notification: Library Cache Object loaded into SGA
Heap size 3823K exceeds notification threshold (2048K)
KGL object name :with sqlplan as (
    select c006 object_owner, c007 object_type,c008 object_name
      from htmldb_collections
     where COLLECTION_NAME='HTMLDB_QUERY_PLAN'
       and c007 in ('TABLE','INDEX','MATERIALIZED VIEW','INDEX (UNIQUE)')),
ws_schemas as(
    select schema 
      from wwv_flow_company_schemas
     where security_group_id = :flow_security_group_id),
t as(
        select s.object_owner table_owner,s.object_name table_name,
               d.OBJECT_ID
          from sqlplan s,sys.dba_objects d

似乎与 SGA 大小有关,但谷歌在这方面没有给我太多帮助。

有人对 TO_NUMBER 或大数据 oracle 函数的这个问题有任何想法吗?

I have been getting an intermittent issue when executing to_number function in the where clause on a varchar2 column if number of records exceed a certain number n. I used n as there is no exact number of records on which it happens. On one DB it happens after n was 1 million on another when it was 0.1. million.

E.g. I have a table with 10 million records say Table Country which has field1 varchar2 containing numberic data and Id

If I do a query as an example

select * 
from country 
where to_number(field1) = 23
and id >1 and id < 100000

This works

But if I do the query

select * 
from country 
where to_number(field1) = 23 
and id >1 and id < 100001

It fails saying invalid number

Next I try the query

select * 
from country
where to_number(field1) = 23 
and id >2 and id < 100001

It works again

As I only got invalid number it was confusing, but in the log file it said

Memory Notification: Library Cache Object loaded into SGA
Heap size 3823K exceeds notification threshold (2048K)
KGL object name :with sqlplan as (
    select c006 object_owner, c007 object_type,c008 object_name
      from htmldb_collections
     where COLLECTION_NAME='HTMLDB_QUERY_PLAN'
       and c007 in ('TABLE','INDEX','MATERIALIZED VIEW','INDEX (UNIQUE)')),
ws_schemas as(
    select schema 
      from wwv_flow_company_schemas
     where security_group_id = :flow_security_group_id),
t as(
        select s.object_owner table_owner,s.object_name table_name,
               d.OBJECT_ID
          from sqlplan s,sys.dba_objects d

It seems its related to SGA size, but google did not give me much help on this.

Does anyone have any idea about this issue with TO_NUMBER or oracle functions for large data?

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

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

发布评论

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

评论(4

我一直都在从未离去 2024-09-07 19:01:00

其中 field1 varchar2 包含
数字数据

这不是一个好的做法。数字数据应保存在 NUMBER 列中。原因很简单:如果我们不强制执行强数据类型,我们可能会发现 varchar2 列中包含非数字数据。如果发生这种情况,那么像这样的过滤器

where to_number(field1) = 23 

将失败,并显示 ORA-01722: invalid number

我不能肯定地说这就是您的场景中发生的情况,因为我不明白为什么 ID 过滤器中明显微不足道的更改会改变查询的成功。查看不同版本的查询的执行计划将会很有帮助。但我认为这更有可能是你的数据问题而不是 SGA 中的错误。

which has field1 varchar2 containing
numberic data

This is not good practice. Numeric data should be kept in NUMBER columns. The reason is simple: if we don't enforce a strong data type we might find ourselves with non-numeric data in our varchar2 column. If that were to happen then a filter like this

where to_number(field1) = 23 

would fail with ORA-01722: invalid number.

I can't for certain sure say this is what is happening in your scenario, because I don't understand why apparently insignificant changes in the filters of ID have changed the success of the query. It would be instructive to see the execution plans for the different versions of the queries. But I think it is more likely to be a problem with your data than a bug in the SGA.

櫻之舞 2024-09-07 19:01:00

假设您知道给定的 id 范围将始终导致 field1 包含数字数据,您可以这样做:

select *
from (
  select /*+NO_MERGE*/ * 
  from country 
  where id >1 and id < 100000
)
where to_number(field1) = 23;

Assuming you know that the given range of ids will always result in field1 containing numeric data, you could do this instead:

select *
from (
  select /*+NO_MERGE*/ * 
  from country 
  where id >1 and id < 100000
)
where to_number(field1) = 23;
烟燃烟灭 2024-09-07 19:01:00

建议执行以下操作以确定是否存在包含非数字数据的记录。正如其他人所说,执行计划和评估顺序的变化可以解释为什么错误的出现不一致。

(假设 SQLPlus 作为客户端)

SET SERVEROUTPUT ON

DECLARE
   x  NUMBER;
BEGIN
  FOR rec IN (SELECT id, field1 FROM country) LOOP
    BEGIN
      x := TO_NUMBER( rec.field1 );
    EXCEPTION
      WHEN OTHERS THEN
        dbms_output.put_line( rec.id || ' ' || rec.field1 );
    END;
  END LOOP;
END;
/

原始问题的替代解决方法是重写查询以避免隐式类型转换,例如

SELECT id, TO_NUMBER( field1 )
  FROM county
  WHERE field1 = '23'
    AND <whatever condition on id you want, if any>

Suggest doing the following to determine for sure whether there are records containing non-numeric data. As others have said, variations in the execution plan and order of evaluation could explain why the error does not appear consistently.

(assuming SQLPlus as the client)

SET SERVEROUTPUT ON

DECLARE
   x  NUMBER;
BEGIN
  FOR rec IN (SELECT id, field1 FROM country) LOOP
    BEGIN
      x := TO_NUMBER( rec.field1 );
    EXCEPTION
      WHEN OTHERS THEN
        dbms_output.put_line( rec.id || ' ' || rec.field1 );
    END;
  END LOOP;
END;
/

An alternative workaround to your original issue would be to rewrite the query to avoid implicit type conversion, e.g.

SELECT id, TO_NUMBER( field1 )
  FROM county
  WHERE field1 = '23'
    AND <whatever condition on id you want, if any>
快乐很简单 2024-09-07 19:01:00

考虑编写 IS_NUMBER PL/SQL 函数:

CREATE OR REPLACE FUNCTION IS_NUMBER (p_input IN VARCHAR2) RETURN NUMBER 
AS
BEGIN
  RETURN TO_NUMBER (p_input);
EXCEPTION
  WHEN OTHERS THEN RETURN NULL;
END IS_NUMBER;
/

SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('TEST') IS NOT NULL;

  COUNT(*)
----------
         0

SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('123.45') IS NOT NULL;

  COUNT(*)
----------
         1

Consider writing an IS_NUMBER PL/SQL function:

CREATE OR REPLACE FUNCTION IS_NUMBER (p_input IN VARCHAR2) RETURN NUMBER 
AS
BEGIN
  RETURN TO_NUMBER (p_input);
EXCEPTION
  WHEN OTHERS THEN RETURN NULL;
END IS_NUMBER;
/

SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('TEST') IS NOT NULL;

  COUNT(*)
----------
         0

SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('123.45') IS NOT NULL;

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