3

次のシナリオを検討してください。stupid_table制御できないスキーマにテーブル (a ) があります。立ち入り禁止の第三者です。触らない。クエリはできますが、インデックスや新しいテーブルを追加したり、デザインを変更したりすることはできません。

のすべての列はstupid_tableです。多くの列がありますが、必要なのはとVARCHAR2(50 BYTE)の 2 つだけです。には整数の文字列表現が取り込まれますが、が に設定されている場合のみ、0 より大きいマジック ナンバーのみが必要です。row_typemagic_numbermagic_numberrow_type'DATA'

SELECT TO_NUMBER(magic_number)
FROM stupid_table
WHERE row_type = 'DATA'
AND TO_NUMBER(magic_number) > 0;

これにより、"無効な数値" という Oracle エラーが発生します。これは、Cost Based Optimiser (CBO) が をTO_NUMBERチェックする前にを評価することを選択しており、フィールドの使用方法が異なる多数row_typeの行があるためです。row_typemagic_number

では、最初に行をフィルター処理してから比較するとどうなるでしょうか?

SELECT TO_NUMBER(t.magic_number)
FROM (
    SELECT magic_number
    FROM stupid_table
    WHERE row_type = 'DATA'
) t
AND TO_NUMBER(t.magic_number) > 0;

現在、CBO は、クエリが非常に単純であり、私が採用した狡猾さを無視して、元のクエリ プランと同じクエリ プランを生成していることに気付いたようです。

最後に、フラストレーションを感じて、私は汚いハックに頼り/*+RULE*/ます。クエリ ヒントを使用して、Oracle に古いルール ベースのオプティマイザーを強制的に使用させます。これは夢のように機能しますが、サポートされなくなった Oracle の機能を使用していることは言うまでもなく、必要ではありません。

これを行うより良い方法はありますか?

4

8 に答える 8

4

CASE に任せよう

select to_number(magic_number) 
from stupid_table
where row_type = 'DATA'
and case when row_type = 'DATA' then to_number(magic_number) else 0 end > 0

私のテストケースでは、エラーを再現するのに問題があったので、DATA番号が含まれていない行があるのではないかと思います. しかし、オプティマイザーがクエリを処理する方法である可能性もあります。

no_merge ヒントも問題を解決する可能性があると思いますが、問題の再現に問題があったため、確信が持てません。

SELECT --+ no_merge(t)
  TO_NUMBER(t.magic_number)
FROM (
    SELECT magic_number
    FROM mike_temp_stupid_table
    WHERE row_type = 'DATA'
) t
where TO_NUMBER(t.magic_number) > 0;
于 2010-11-11T18:27:41.440 に答える
4

例外を飲み込む独自の変換関数を作成することでこれを解決します。

CREATE OR REPLACE FUNCTION my_to_number( p_str IN VARCHAR2 )
  RETURN number
IS 
BEGIN
  RETURN to_number( p_str );
EXCEPTION
  WHEN OTHERS THEN
    RETURN null;
END;

次に、クエリを変更します

SELECT TO_NUMBER(magic_number)
FROM stupid_table
WHERE row_type = 'DATA'
AND MY_TO_NUMBER(magic_number) > 0;

それがなければ、RBO によって生成されたクエリ プランを使用して、CBO がそのプランを使用するように強制するプロファイルを作成できます。これは、CBO が ROW_TYPE 述語の前に MAGIC_NUMBER 述語を適用することを防ぐヒントの完全なセットを提供しようとするよりも、おそらく管理が少し簡単です。

于 2010-11-11T17:30:09.547 に答える
3

TO_NUMBER完全に使用を避けることはできますか?とにかくパフォーマンスが向上するようです。何かのようなもの:

WHERE t.magic_number != '0'

負の数が存在する可能性がある場合、または数値が浮動小数点である場合は、追加のチェックが必要になる可能性がありますが、それは確かに実行可能です。

于 2010-11-11T17:30:00.227 に答える
3

正確な方法は、ordered_predicatesヒントを使用して、WHERE条件が評価される順序を変更することです。

ドキュメント: Oracle ORDERED_PREDICATES ヒント

SELECT /*+ ORDERED_PREDICATES */ TO_NUMBER(magic_number)
FROM stupid_table
WHERE row_type = 'DATA'
AND TO_NUMBER(magic_number) > 0;

ここで条件を入れ替えてみると、再びエラーが発生します。TO_NUMBER を呼び起こすことが最善の解決策であるとは思えないので、他の回答も検討してください。

于 2011-05-08T20:17:41.963 に答える
2

通常、rownum を追加して、述語のプッシュを停止します。(ヒントもこれを行うことができますが、間違いやすいので、このタイプの問題では、間違っている場合はすぐに気付かない可能性があります。) また、後で誰かが試みないように、おそらくコメントを追加する必要があります。コードを「最適化」し、不要なロジックのように見えるものを削除します。

SELECT TO_NUMBER(t.magic_number)
FROM (
    --Bad data, use rownum for type safety
    SELECT magic_number, rownum
    FROM stupid_table
    WHERE row_type = 'DATA'
) t
AND TO_NUMBER(t.magic_number) > 0;
于 2010-11-12T01:17:10.283 に答える
2

「DATA」のrow_typeのみを含む愚かなテーブルのスライスの具体化されたビューを作成するのはどうですか?

于 2010-11-11T17:30:32.107 に答える
1

withステートメントを使用すると、特定の評価順序を適用できます。

WITH
has_numerics_only AS
(
    SELECT magic_number
    FROM stupid_table
    WHERE row_type = 'DATA'
)
SELECT TO_NUMBER(t.magic_number)
FROM has_numerics_only
WHERE TO_NUMBER(t.magic_number) > 0;

また、1つ以上の「DATA」行に実際に不良データがある可能性も考慮してください。

于 2010-11-11T19:12:35.007 に答える
0

あなたは試すことができます:

SELECT TO_NUMBER(magic_number)
FROM stupid_table
WHERE row_type = 'DATA'
AND REGEXP_LIKE(magic_number, '^\d{1,}$');

それでも問題が解決しない場合は、条件を HAVING 句に移動すると、オプティマイザーが最初に条件を評価するように強制される可能性があります。

SELECT TO_NUMBER(magic_number)
FROM (
SELECT magic_number
FROM stupid_table
WHERE row_type = 'DATA'
GROUP BY magic_number
HAVING REGEXP_LIKE(magic_number, '^\d{1,}$')) ilv;

それができない場合は、マテリアライズド ビューまたは PL/SQL カーソルを使用することが唯一の方法かもしれません。

于 2010-11-11T17:41:58.903 に答える