4

以前、実行計画の「コスト」が相対的な実行時間の良い指標であることがわかりました。なぜこのケースは違うのですか?実行計画に関連性があると考える私はばかですか? v_test のパフォーマンスを改善するには、具体的に何を試すことができますか?

ありがとうございました。

Oracle 10gを使用して、以下に定義された単純なクエリビューがあります

  create or replace view v_test as
  select distinct u.bo_id as bo_id, upper(trim(d.dept_id)) as dept_id
  from
      cust_bo_users u
  join cust_bo_roles r on u.role_name=r.role_name
  join cust_dept_roll_up_tbl d on 
                            (r.region is null or trim(r.region)=trim(d.chrgback_reg)) and 
                            (r.prod_id is null or trim(r.prod_id)=trim(d.prod_id)) and
                            (r.div_id is null or trim(r.div_id)=trim(d.div_id )) and
                            (r.clus_id is null or trim(r.clus_id )=trim( d.clus_id)) and
                            (r.prod_ln_id is null or trim(r.prod_ln_id)=trim(d.prod_ln_id)) and
                            (r.dept_id is null or trim(r.dept_id)=trim(d.dept_id))

次のビューを置き換えるように定義されています

        create or replace view v_bo_secured_detail
  select distinct Q.BO_ID, Q.DEPT_ID
  from (select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'REGION' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_PROD' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD' and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DIV' and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_DIV' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'CLUS' and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_CLUS' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD_LN' and
                trim(R.PROD_LN_ID) = UPPER(trim(D.PROD_LN_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(R.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DEPT') Q

ROLE_LEVEL 列への依存を取り除くことを目的としています。

v_test の実行計画は、単純な v_bo_secured_detail よりも大幅に低くなります。

select * from <view> where bo_id='value'

クエリ。また、実際のクエリで使用すると大幅に低くなります

  select CT_REPORT.RPT_KEY,
         CT_REPORT_ENTRY.RPE_KEY,
         CT_REPORT_ENTRY.CUSTOM16,
         Exp_Sub_Type.value,
         min(CT_REPORT_PAYMENT_CONF.PAY_DATE),
         CT_REPORT.PAID_DATE
  from CT_REPORT,
      <VIEW> SD,
      CT_REPORT_ENTRY,
      CT_LIST_ITEM_LANG Exp_Sub_Type,
      CT_REPORT_PAYMENT_CONF,
      CT_STATUS_LANG Payment_Status
  where (CT_REPORT_ENTRY.RPT_KEY = CT_REPORT.RPT_KEY) and
        (Payment_Status.STAT_KEY = CT_REPORT.PAY_KEY) and
        (Exp_Sub_Type.LI_KEY = CT_REPORT_ENTRY.CUSTOM9 and Exp_Sub_Type.LANG_CODE = 'en') and
        (CT_REPORT.RPT_KEY = CT_REPORT_PAYMENT_CONF.RPT_KEY) and
        (SD.BO_ID = 'JZHU9') and
        (SD.DEPT_ID = UPPER(CT_REPORT_ENTRY.CUSTOM5)) and
        (Payment_Status.name = 'Payment Confirmed' and (Payment_Status.LANG_CODE = 'en') and
        CT_REPORT.PAID_DATE > to_date('01/01/2008', 'mm/dd/yyyy') and Exp_Sub_Type.value != 'Korea')
  group by CT_REPORT.RPT_KEY,
            CT_REPORT_ENTRY.RPE_KEY,
            CT_REPORT_ENTRY.CUSTOM16,
            Exp_Sub_Type.value,
            CT_REPORT.PAID_DATE

実行時間は大幅に異なります。v_test ビューには 15 時間、v_bo_secured_detail には数秒かかります。


回答してくれた皆さんありがとう

これは私にとって覚えておくべき1つです。式の理論と数学がハードウェアベースの実行の現実と出会う場所。ああ。

4

6 に答える 6

4

実行計画は理論であり、実行時間は現実です。

プランは、エンジンがクエリを実行する方法を示していますが、一部の手順では、クエリを解決するために膨大な量の作業が発生する可能性があります。「x is null or x = y」の使用は悪臭を放ちます。r と d が大きなテーブルである場合、ある種の組み合わせの爆発が発生し、ディスク ブロックの大きなリストを無限に循環するリクエストが発生する可能性があります。実行中に多くの I/O が発生していると思います。

一方、結合された選択は短くて甘いので、おそらく以前の選択からまだ残っている多くのディスクブロックを再利用したり、同じディスクブロックの読み取りから恩恵を受けるある程度の並列処理を行ったりします。

また、どこでも trim() と upper() を使用するのは、少し疑わしく見えます。データが非常に汚れている場合は、「x = y」と言ってそれが機能することを確認できるように、定期的なハウスクリーニングを時々実行する価値があるかもしれません。

更新: v_test を改善するためのヒントを求めました。trim() と upper() が不要になるようにデータをクリーンアップします。それらは、インデックスが使用されないようにする可能性があります (ただし、ユニオン化された選択バージョンにも影響します)。

「x is null or x = y」を取り除くことができない場合、y = nvl(x,'does-not-exist') の方が優れた特性を持っている可能性があります (「does-not-exist」が「can」であると仮定すると)起こりません」 id 値)。

于 2008-09-19T15:22:03.983 に答える
3

Oracle のドキュメントにあるように、コストは特定の実行計画に関連する推定コストです。クエリを微調整すると、コストが相対的に計算される特定の実行計画が変更される可能性があります。時には劇的に。

v_test のパフォーマンスの問題は、ネストされたループを実行する以外に Oracle が v_test を実行する方法を考えられないことです。それぞれの cust_bo_roles に対して、cust_dept_roll_up_tbl のすべてをスキャンして一致を見つけます。テーブルのサイズが n と m の場合、これには n*m 時間がかかり、大きなテーブルでは時間がかかります。対照的に、v_bo_secured_detail は一連のクエリになるように設定されており、それぞれが他のメカニズムを介して実行できます。(Oracle には、インデックスの使用、オンザフライでのハッシュの構築、データセットの並べ替えとマージなど、使用できる数値があります。これらの操作はすべて O(n*log(n)) またはそれ以上です。)高速クエリは高速です。

大変なことですが、このクエリを高速にしたい場合は、前のクエリのように分割する必要があります。

于 2008-09-19T15:23:35.663 に答える
0

基礎となるすべてのテーブルでオプティマイザー統計を収集しましたか? それらがなければ、オプティマイザーの見積もりは現実とは大きくかけ離れている可能性があります。

于 2008-09-19T15:22:58.740 に答える
0

「クエリ プランが低い」とは、短いということですか、それとも実際のコスト見積もりが低いということですか。置換ビューの明らかな問題の 1 つは、 cust_dept_roll_up_tbl を使用した結合では、ほぼ排他的にインデックス付けできない基準が使用されることです (「is null」テストはインデックスで満たすことができますが、各引数でトリムを呼び出すことを含むテストは満たすことができません)。クエリを満たすには、少なくとも 1 回、おそらく複数回のテーブルの順次スキャンを行う必要があります。

Oracle にこの制限があるかどうかはわかりませんが、多くの DB では含まれるテーブルごとに 1 つのインデックス スキャンしか実行できないため、結合条件をクリーンアップしてインデックス可能にしても、インデックスで 1 つの条件しか満たさない可能性があります。スキャンし、残りのシーケンシャル スキャンを使用する必要があります。

于 2008-09-19T15:28:15.667 に答える
0

低コスト - 長い実行時間の 1 つの側面は、大規模なデータ セットを調べている場合、多くの場合、まとめて処理する方が全体的に効率的であるということです。一方、迅速な結果が必要な場合は、最初のレコードを取得するためにできる限り少ない作業を行います。大きなセットで作業する場合、迅速な応答のように見える小さな操作を繰り返し行うと、良い結果が得られない可能性があります。

多くの場合、迅速な結果が必要な場合は、USE_NL オプティマイザー ヒントが役立ちます。

また、テスト ビューでは、IS NULL に依存しています... IS NULL はインデックスを使用することも、「テーブル側」パラメーターでトリムなどの関数を使用することもできません。

于 2008-09-19T15:21:12.813 に答える
0

コストについて少し詳しく説明します。

Oracle 9/10g では、少し単純化すると、コストは次の式で決定されます。

コスト = (SrCount * SrTime + MbrCount * MbrTime + CpuCyclesCount * CpuCycleTime) / SrTime

ここで、SrCount - 行われた単一ブロック読み取りの合計数、SrTime - 収集されたシステム統計に基づく単一ブロック読み取りの平均時間、MbrCount および MbrTime、対応するマルチブロック読み取り (フル テーブル スキャンおよびインデックス高速フル スキャン中に使用されるもの)、Cpu に基づく関連するメトリックは自明であり、すべて単一ブロックの読み取り時間で割ったものです。

于 2011-11-18T03:41:45.687 に答える