4

I have a table where two columns are of type VARCHAR2(3BYTE) and VARCHAR2(32BYTE). When I do a select query (where col1=10 and where col1='10') or (where col2=70001 or col2='70001') the number of records fetched are the same in each set of where clauses. How does this happen? How does Oracle treat string literals and numeric constants and compare to the data despite column data-type?

But this does not work for a column of type VARCHAR2(128BYTE). The query needed to be where col3='55555555001' to work and where col3=55555555001 throws ORA-01722 error.

4

2 に答える 2

13

SQL 言語リファレンスに記載されているとおり:

  • SELECT FROM 操作中に、Oracle はデータを列からターゲット変数の型に変換します。
  • ...
  • 文字値を数値と比較する場合、Oracle は文字データを数値に変換します。

型が一致しない場合、テーブルの列に対して暗黙的な変換が実行されます。これは、いくつかのダミー データを使用して SQL*Plus でトレースすることで確認できます。

create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain

これは機能します:

select * from t42 where foo = '10';

FOO
---
10

Execution Plan
----------------------------------------------------------
Plan hash value: 3843907281

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T42  |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("FOO"='10')

Note
-----
   - dynamic sampling used for this statement (level=2)

しかし、このエラー:

select * from t42 where foo = 10;

ERROR:
ORA-01722: invalid number



Execution Plan
----------------------------------------------------------
Plan hash value: 3843907281

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T42  |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(TO_NUMBER("FOO")=10)

フィルターの違いに注意してください。filter("FOO"='10')filter(TO_NUMBER("FOO")=10)。後者の場合、数値と比較するとto_number()、テーブル内のすべての行に対して実行され、その変換の結果が固定値と比較されます。そのため、変換できない文字値があると、ORA-01722 が返されます。適用される関数は、その列にインデックスが存在する場合、使用中のインデックスも停止します。

興味深いのは、複数のフィルターがある場合です。Oracle は異なるタイミングで異なる順序でそれらを評価する場合があるため、常に ORA-01722 が表示されるとは限らず、時々ポップアップすることがあります。あなたが持っていたとしましょうwhere foo = 10 and bar = 'X'。オラクルが非値を最初に除外できると考えた場合、残ったものXにのみ を適用しto_number()、その小さなサンプルには に非数値が含まれていない可能性がありますfoo。ただし、 がある場合、値がどれだけ選択的であるかに応じてand bar = 'Y'、非Y値に非数値が含まれるか、 Oracle がfoo 最初に をフィルタリングする可能性があります。

教訓は、数値情報を文字型として保存しないことです。


モラルをバックアップするためにAskTomのリファレンスを探していました.最初に見たものは、「述語の順序の変更」の効果と「varchar2に数値を保存しないでください」と言っている.

于 2013-01-24T08:19:26.763 に答える
2

数値列または値と文字列が関係している場合、Oracle は文字列の値を数値に変換してから、数値を数値に変換します。それはあなたが書いたかのようです:

where to_number(col3) = 55555555001

そのためORA-01722: invalid number、1 つの行に数値に変換できない文字列 (n col3) が含まれていると、エラーが発生します。

そのためIS_NUMBER、Oracle データベースには、エラーを発生させず、数値に変換できない値に対して NULL を返す関数があります。次に、安全に書くことができます:

where is_number(col3) = 55555555001

関数は次のように定義されます。

CREATE OR REPLACE FUNCTION is_number (p_str IN VARCHAR2)
  RETURN NUMBER
IS
  l_num NUMBER;
BEGIN
  l_num := to_number(p_str);
  RETURN l_num;

EXCEPTION
  WHEN others THEN
    RETURN NULL;
END is_number;
于 2013-01-24T08:18:48.537 に答える