2010-05-06 40 views
2

如果記錄數超過特定數n,那麼在varchar2列的where子句中執行to_number函數時,我得到間歇性問題。我使用n,因爲它沒有確切的記錄數量。在一個數據庫中,它發生在n爲100萬而另一個爲0.1時。百萬。Oracle中的TO_NUMBER函數出現奇怪問題

E.g.我有一千萬條記錄表說,表國傢俱有含數字小數據和Id

如果我做一個查詢爲例

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

這工作

但field1的VARCHAR2,如果我做查詢

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功能的大數據?

回答

2

假設你知道ID的給定範圍內總是會導致含有數字數據字段1,你可以這樣做,而不是:

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

可能需要添加一個提示來防止將外部謂詞合併到內聯視圖中。 – 2010-05-07 13:25:16

+0

@Dave:謝謝,沒錯 - 添加 – 2010-05-07 13:52:22

4

,其具有包含 數字小數據

FIELD1 VARCHAR2這不是好的做法。數字數據應保存在NUMBER列中。原因很簡單:如果我們不強制使用強數據類型,我們可能會在varchar2列中找到非數字數據。如果是這樣,然後發生過濾器這樣

where to_number(field1) = 23 

會失敗ORA-01722: invalid number

我不能肯定地說這是你的情況發生了什麼,因爲我不明白爲什麼在ID的過濾器中顯然微不足道的變化改變了查詢的成功。查看不同版本查詢的執行計劃將是有益的。但是我認爲它更可能是數據問題,而不是SGA中的錯誤。

+0

又豈是數據?他的榜樣似乎排除了這一點。 – 2010-05-06 23:55:33

+4

查詢中沒有任何內容規定謂詞的順序。某些查詢可能會首先對id進行過濾,以及何時排除行,因爲它們不匹配to_number(),因此永遠不會對該行進行評估。其他人可能會在id之前應用to_number(),並且同一行產生錯誤。 – 2010-05-07 00:05:33

+3

值的範圍的大小可能會導致優化程序更改謂詞的評估順序;在這個例子中它超過了數據的1%,這聽起來很低,但是統計數據比實際的行數更重要。因此,在小範圍內,它可能使用id上的索引,在很大的範圍內,它可能會忽略索引並執行完整掃描,正如@Gary所說的那樣,在無效數字varchar2值上打上to_number()。 – 2010-05-07 00:12:48

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> 
+0

如果field1 ='023'會怎麼樣? – 2010-05-07 21:33:25

+0

@Charlie:好的褲子...... LTRIM(field1,'0')='23'' :) – 2010-10-09 09:00:46

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