4

如果记录数超过某个数字 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 函数的这个问题有任何想法吗?

4

4 回答 4

5

其中有 field1 varchar2 包含数字数据

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

where to_number(field1) = 23 

会失败ORA-01722: invalid number

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

于 2010-05-06T23:16:18.387 回答
3

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

select *
from (
  select /*+NO_MERGE*/ * 
  from country 
  where id >1 and id < 100000
)
where to_number(field1) = 23;
于 2010-05-07T01:41:20.040 回答
1

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

(假设 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>
于 2010-05-07T13:24:46.610 回答
0

考虑编写一个 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
于 2010-05-10T17:59:44.117 回答