【问题标题】:Getting weird issue with TO_NUMBER function in OracleOracle 中的 TO_NUMBER 函数出现奇怪问题
【发布时间】:2011-02-16 14:54:36
【问题描述】:

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

例如我有一个包含 1000 万条记录的表,比如 Table Country,它的 field1 varchar2 包含数字数据和 Id

如果我以查询为例

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 函数的这个问题有任何想法吗?

【问题讨论】:

    标签: sql oracle oracle10g ora-01722


    【解决方案1】:

    考虑编写一个 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
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 可能需要添加一个提示来防止外部谓词合并到内联视图中。
      【解决方案3】:

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

      (假设 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>
      

      【讨论】:

      • @Charlie:好吧聪明的裤子...LTRIM(field1,'0') = '23' :)
      【解决方案4】:

      其中有 field1 varchar2 包含 数字数据

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

      where to_number(field1) = 23 
      

      会因ORA-01722: invalid number 而失败。

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

      【讨论】:

      • 怎么会是数据?他的例子似乎排除了这一点。
      • 查询中没有任何内容规定谓词的顺序。某些查询可能首先过滤 id 并且当行被排除时,因为它们不匹配 to_number() 永远不会为该行评估。其他人可能会在 id 之前应用 to_number() 并且同一行会产生错误。
      • 值范围的大小可能会导致优化器改变谓词的评估顺序;在示例中,它超过了数据的 1%,这听起来很低,但统计数据比实际的行数更重要。因此,在较小的范围内,它可能会使用 id 上的索引,在较大的范围内,它可能会忽略索引并进行全面扫描,在途中遇到一个无效数字 varchar2 值的 to_number(),正如@Gary 所说。跨度>
      • +1 给 Gary 和 Alex - 主要线索是“如果记录数超过一定数量 n” - 有一个阈值可以改变计划。这个阈值可能对每个实例都是唯一的,并且会受到表统计信息和实际数据的影响。
      • 我正确地查看了数据库,这是我们创建的一个测试表,它肯定只在 field1 中包含数字数据。
      猜你喜欢
      • 1970-01-01
      • 2017-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 2012-01-30
      • 1970-01-01
      • 2016-04-20
      相关资源
      最近更新 更多