2

実行速度が非常に遅いクエリのtkprof出力は次のとおりです(警告:長いです:-)):

SELECT mbr_comment_idn, mbr_crt_dt, mbr_data_source, mbr_dol_bl_rmo_ind, mbr_dxcg_ctl_member, mbr_employment_start_dt, mbr_employment_term_dt, mbr_entity_active, mbr_ethnicity_idn, mbr_general_health_status_code, mbr_hand_dominant_code, mbr_hgt_feet, mbr_hgt_inches, mbr_highest_edu_level, mbr_insd_addr_idn, mbr_insd_alt_id, mbr_insd_name, mbr_insd_ssn_tin, mbr_is_smoker, mbr_is_vip, mbr_lmbr_first_name, mbr_lmbr_last_name, mbr_marital_status_cd, mbr_mbr_birth_dt, mbr_mbr_death_dt, mbr_mbr_expired, mbr_mbr_first_name, mbr_mbr_gender_cd, mbr_mbr_idn, mbr_mbr_ins_type, mbr_mbr_isreadonly, mbr_mbr_last_name, mbr_mbr_middle_name, mbr_mbr_name, mbr_mbr_status_idn, mbr_mpi_id, mbr_preferred_am_pm, mbr_preferred_time, mbr_prv_innetwork, mbr_rep_addr_idn, mbr_rep_name, mbr_rp_mbr_id, mbr_same_mbr_ins, mbr_special_needs_cd, mbr_timezone, mbr_upd_dt, mbr_user_idn, mbr_wgt, mbr_work_status_idn 
FROM (SELECT /*+ FIRST_ROWS(1) */ mbr_comment_idn, mbr_crt_dt, mbr_data_source, mbr_dol_bl_rmo_ind, mbr_dxcg_ctl_member, mbr_employment_start_dt, mbr_employment_term_dt, mbr_entity_active, mbr_ethnicity_idn, mbr_general_health_status_code, mbr_hand_dominant_code, mbr_hgt_feet, mbr_hgt_inches, mbr_highest_edu_level, mbr_insd_addr_idn, mbr_insd_alt_id, mbr_insd_name, mbr_insd_ssn_tin, mbr_is_smoker, mbr_is_vip, mbr_lmbr_first_name, mbr_lmbr_last_name, mbr_marital_status_cd, mbr_mbr_birth_dt, mbr_mbr_death_dt, mbr_mbr_expired, mbr_mbr_first_name, mbr_mbr_gender_cd, mbr_mbr_idn, mbr_mbr_ins_type, mbr_mbr_isreadonly, mbr_mbr_last_name, mbr_mbr_middle_name, mbr_mbr_name, mbr_mbr_status_idn, mbr_mpi_id, mbr_preferred_am_pm, mbr_preferred_time, mbr_prv_innetwork, mbr_rep_addr_idn, mbr_rep_name, mbr_rp_mbr_id, mbr_same_mbr_ins, mbr_special_needs_cd, mbr_timezone, mbr_upd_dt, mbr_user_idn, mbr_wgt, mbr_work_status_idn, ROWNUM AS ora_rn 
FROM (SELECT mbr.comment_idn AS mbr_comment_idn, mbr.crt_dt AS mbr_crt_dt, mbr.data_source AS mbr_data_source, mbr.dol_bl_rmo_ind AS mbr_dol_bl_rmo_ind, mbr.dxcg_ctl_member AS mbr_dxcg_ctl_member, mbr.employment_start_dt AS mbr_employment_start_dt, mbr.employment_term_dt AS mbr_employment_term_dt, mbr.entity_active AS mbr_entity_active, mbr.ethnicity_idn AS mbr_ethnicity_idn, mbr.general_health_status_code AS mbr_general_health_status_code, mbr.hand_dominant_code AS mbr_hand_dominant_code, mbr.hgt_feet AS mbr_hgt_feet, mbr.hgt_inches AS mbr_hgt_inches, mbr.highest_edu_level AS mbr_highest_edu_level, mbr.insd_addr_idn AS mbr_insd_addr_idn, mbr.insd_alt_id AS mbr_insd_alt_id, mbr.insd_name AS mbr_insd_name, mbr.insd_ssn_tin AS mbr_insd_ssn_tin, mbr.is_smoker AS mbr_is_smoker, mbr.is_vip AS mbr_is_vip, mbr.lmbr_first_name AS mbr_lmbr_first_name, mbr.lmbr_last_name AS mbr_lmbr_last_name, mbr.marital_status_cd AS mbr_marital_status_cd, mbr.mbr_birth_dt AS mbr_mbr_birth_dt, mbr.mbr_death_dt AS mbr_mbr_death_dt, mbr.mbr_expired AS mbr_mbr_expired, mbr.mbr_first_name AS mbr_mbr_first_name, mbr.mbr_gender_cd AS mbr_mbr_gender_cd, mbr.mbr_idn AS mbr_mbr_idn, mbr.mbr_ins_type AS mbr_mbr_ins_type, mbr.mbr_isreadonly AS mbr_mbr_isreadonly, mbr.mbr_last_name AS mbr_mbr_last_name, mbr.mbr_middle_name AS mbr_mbr_middle_name, mbr.mbr_name AS mbr_mbr_name, mbr.mbr_status_idn AS mbr_mbr_status_idn, mbr.mpi_id AS mbr_mpi_id, mbr.preferred_am_pm AS mbr_preferred_am_pm, mbr.preferred_time AS mbr_preferred_time, mbr.prv_innetwork AS mbr_prv_innetwork, mbr.rep_addr_idn AS mbr_rep_addr_idn, mbr.rep_name AS mbr_rep_name, mbr.rp_mbr_id AS mbr_rp_mbr_id, mbr.same_mbr_ins AS mbr_same_mbr_ins, mbr.special_needs_cd AS mbr_special_needs_cd, mbr.timezone AS mbr_timezone, mbr.upd_dt AS mbr_upd_dt, mbr.user_idn AS mbr_user_idn, mbr.wgt AS mbr_wgt, mbr.work_status_idn AS mbr_work_status_idn 
FROM mbr JOIN mbr_identfn ON mbr.mbr_idn = mbr_identfn.mbr_idn 
WHERE mbr_identfn.mbr_idn = mbr.mbr_idn AND mbr_identfn.identfd_type = :identfd_type_1 AND mbr_identfn.identfd_number = :identfd_number_1 AND mbr_identfn.entity_active = :entity_active_1) 
WHERE ROWNUM <= :ROWNUM_1) 
WHERE ora_rn > :ora_rn_1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse     9936      0.46       0.49          0          0          0           0
Execute   9936      0.60       0.59          0          0          0           0
Fetch     9936    329.87     404.00          0  136966922          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    29808    330.94     405.09          0  136966922          0           0

Misses in library cache during parse: 0
Optimizer mode: FIRST_ROWS
Parsing user id: 36  (JIVA_DEV)

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  VIEW  (cr=102 pr=0 pw=0 time=2180 us)
      0   COUNT STOPKEY (cr=102 pr=0 pw=0 time=2163 us)
      0    NESTED LOOPS  (cr=102 pr=0 pw=0 time=2152 us)
      0     INDEX SKIP SCAN IDX_MBR_IDENTFN (cr=102 pr=0 pw=0 time=2140 us)(object id 341053)
      0     TABLE ACCESS BY INDEX ROWID MBR (cr=0 pr=0 pw=0 time=0 us)
      0      INDEX UNIQUE SCAN PK_CLAIMANT (cr=0 pr=0 pw=0 time=0 us)(object id 334044)


Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: HINT: FIRST_ROWS
      0   VIEW
      0    COUNT (STOPKEY)
      0     NESTED LOOPS
      0      INDEX   MODE: ANALYZED (SKIP SCAN) OF 'IDX_MBR_IDENTFN' 
                 (INDEX (UNIQUE))
      0      TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF 'MBR' 
                 (TABLE)
      0       INDEX   MODE: ANALYZED (UNIQUE SCAN) OF 'PK_CLAIMANT' 
                  (INDEX (UNIQUE))

********************************************************************************

オラクルのスキップスキャンに関するドキュメントを読んだところによると、スキップスキャンは、インデックスの最初の列の一意の値の数が少ない場合に最も役立ちます。重要なのは、この列の最初のインデックスには多数の一意性があるということです。だから私はスキップスキャンがここで行うのは間違っていると仮定して正しいですか?また、どのようなスキャンを行う必要がありますか?このクエリに対してもう少しヒントを与える必要がありますか?

編集IDX_MBR_IDENTFN:クエリのwhere句は、そのインデックスにあるもの以外の列ではなく、の列を使用することも指摘しておく必要があります。私が知る限り、私はどの列もスキップしていません。

編集2:このクエリを高速化するためにいくつかのことを行いました。まず、ページングを削除しました。結局のところ、このクエリはとにかく1行しか返しません。次に、LEADINGテーブルが正しい順序でクエリされていることを確認するためのヒントを追加しました。第三に、重複するmbr_idn述語を削除しました。最後に、私はIDX_MBR_IDENTFNユニークにしました。全体として、これによりパフォーマンスが大幅に向上します(ただし、実行しているクエリの中で最もコストがかかります)。

SELECT /*+ LEADING (mbr_identfn, mbr) */ mbr.comment_idn AS mbr_comment_idn, mbr.crt_dt AS mbr_crt_dt, mbr.data_source AS mbr_data_source, mbr.dol_bl_rmo_ind AS mbr_dol_bl_rmo_ind, mbr.dxcg_ctl_member AS mbr_dxcg_ctl_member, mbr.employment_start_dt AS mbr_employment_start_dt, mbr.employment_term_dt AS mbr_employment_term_dt, mbr.entity_active AS mbr_entity_active, mbr.ethnicity_idn AS mbr_ethnicity_idn, mbr.general_health_status_code AS mbr_general_health_status_code, mbr.hand_dominant_code AS mbr_hand_dominant_code, mbr.hgt_feet AS mbr_hgt_feet, mbr.hgt_inches AS mbr_hgt_inches, mbr.highest_edu_level AS mbr_highest_edu_level, mbr.insd_addr_idn AS mbr_insd_addr_idn, mbr.insd_alt_id AS mbr_insd_alt_id, mbr.insd_name AS mbr_insd_name, mbr.insd_ssn_tin AS mbr_insd_ssn_tin, mbr.is_smoker AS mbr_is_smoker, mbr.is_vip AS mbr_is_vip, mbr.lmbr_first_name AS mbr_lmbr_first_name, mbr.lmbr_last_name AS mbr_lmbr_last_name, mbr.marital_status_cd AS mbr_marital_status_cd, mbr.mbr_birth_dt AS mbr_mbr_birth_dt, mbr.mbr_death_dt AS mbr_mbr_death_dt, mbr.mbr_expired AS mbr_mbr_expired, mbr.mbr_first_name AS mbr_mbr_first_name, mbr.mbr_gender_cd AS mbr_mbr_gender_cd, mbr.mbr_idn AS mbr_mbr_idn, mbr.mbr_ins_type AS mbr_mbr_ins_type, mbr.mbr_isreadonly AS mbr_mbr_isreadonly, mbr.mbr_last_name AS mbr_mbr_last_name, mbr.mbr_middle_name AS mbr_mbr_middle_name, mbr.mbr_name AS mbr_mbr_name, mbr.mbr_status_idn AS mbr_mbr_status_idn, mbr.mpi_id AS mbr_mpi_id, mbr.preferred_am_pm AS mbr_preferred_am_pm, mbr.preferred_time AS mbr_preferred_time, mbr.prv_innetwork AS mbr_prv_innetwork, mbr.rep_addr_idn AS mbr_rep_addr_idn, mbr.rep_name AS mbr_rep_name, mbr.rp_mbr_id AS mbr_rp_mbr_id, mbr.same_mbr_ins AS mbr_same_mbr_ins, mbr.special_needs_cd AS mbr_special_needs_cd, mbr.timezone AS mbr_timezone, mbr.upd_dt AS mbr_upd_dt, mbr.user_idn AS mbr_user_idn, mbr.wgt AS mbr_wgt, mbr.work_status_idn AS mbr_work_status_idn 
FROM mbr JOIN mbr_identfn ON mbr.mbr_idn = mbr_identfn.mbr_idn 
WHERE mbr_identfn.identfd_type = :identfd_type_1 AND mbr_identfn.identfd_number = :identfd_number_1 AND mbr_identfn.entity_active = :entity_active_1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse    10102      0.45       0.42          0          0          0           0
Execute  10102      0.44       0.52          0          0          0           0
Fetch    10102      1.60       1.81          0     218121          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    30306      2.50       2.75          0     218121          0           0

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 36  (JIVA_DEV)

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  NESTED LOOPS  (cr=3 pr=0 pw=0 time=96 us)
      0   TABLE ACCESS BY INDEX ROWID MBR_IDENTFN (cr=3 pr=0 pw=0 time=88 us)
      0    INDEX UNIQUE SCAN UK_CLM_IDFN (cr=3 pr=0 pw=0 time=77 us)(object id 334118)
      0   TABLE ACCESS BY INDEX ROWID MBR (cr=0 pr=0 pw=0 time=0 us)
      0    INDEX UNIQUE SCAN PK_CLAIMANT (cr=0 pr=0 pw=0 time=0 us)(object id 334044)


Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      0   NESTED LOOPS
      0    TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF 
               'MBR_IDENTFN' (TABLE)
      0     INDEX   MODE: ANALYZED (UNIQUE SCAN) OF 'UK_CLM_IDFN' (INDEX 
                (UNIQUE))
      0    TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF 'MBR' (TABLE)

      0     INDEX   MODE: ANALYZED (UNIQUE SCAN) OF 'PK_CLAIMANT' (INDEX 
                (UNIQUE))
4

4 に答える 4

7

インデックス スキップ スキャンとは、インデックスの最初の列が無視されることを意味します。Oracle は最初の列のすべての項目を読み取り、2 番目 (または 3 番目など) の列が検索対象であるかどうかを確認するため、これによりパフォーマンスが低下します。これは通常、全テーブル スキャンよりも高速ですが (クエリによって異なります)、インデックス レンジ スキャンよりも低速です。

クエリの一部であり、クエリで使用されている列に別のインデックスを作成してみてくださいIDX_MBR_IDENTFN


たとえば、your_table次のようになっているとします。

id  status
1   0
2   0
3   0
4   1

に複合インデックスが(id, status)あり、クエリSelect * From your_table Where status = 1はインデックスを使用する可能性がありますが、正しい行を見つけるために、インデックス (id1から4) のすべての行を読み取り、status.


更新: 次のインデックスを使用すると、パフォーマンスがさらに向上する可能性がありますが、実際に役立つかどうかを試す必要があります。

mbr_identfn( identfd_type, identfd_number, entity_active, mbr_idn )

これは、ヒントを回避するのにも役立ちます。

于 2010-05-05T21:28:21.417 に答える
6

スキップスキャンから焦点を移します。

tkprof スニペットは、このステートメントを発行する回数を減らすことが最優先事項であることを示しています。現在、このステートメントを 9936 回実行しています。そして、各実行には 405/9936 秒しかかかりません。適度に速い。ただし、9936回実行した場合はそうではありません。

したがって、このステートメントはループ構成内でほぼ確実です。各反復で、異なる入力パラメーター セット (:identfd_type_1、:identfd_number_1、:entity_active_1、:ROWNUM_1、:ora_rn_1) を指定します。このループ構成を書き直して、このステートメントをセット全体に対して 1 回実行すると、パフォーマンスの問題はおそらく過去のものになります。そうでない場合は、新しい tkprof 出力を投稿してください。

よろしく、ロブ。

于 2010-05-06T08:48:25.940 に答える
1

スキップスキャンを説明するために...これはクエリの関連する述語部分のようです。

WHERE mbr_identfn.mbr_idn = mbr.mbr_idn
  AND mbr_identfn.identfd_type = :identfd_type_1
  AND mbr_identfn.identfd_number = :identfd_number_1
  AND mbr_identfn.entity_active = :entity_active_1

実行がMBR_IDENTFNで始まる場合、インデックスで検索するMBR_IDNの値はまだありません。これは、一意のスキャンまたは範囲スキャンを実行できないことを意味します。ただし、インデックスの他の3つの列には(バインド変数として)値が指定されているため、スキップスキャンを実行できます。Oracleは、ベーステーブルへのアクセスをまったく回避するためにこれを行うことを選択していますが、これは理にかなっているようです。

MBR_IDENTFNの主キーは何ですか?MBR_IDNだけですか?

IDENTFD_TYPE、IDENTFD_NUMBER、およびENTITY_ACTIVEの一部またはすべてを先頭の列として、MBR_IDENTFNに個別のインデックスを作成する必要があると思います。これにより、スキップスキャンの代わりに範囲スキャンまたは一意のスキャンを実行できます。

于 2010-05-06T15:09:04.413 に答える
1

インデックス内の列 (PK_CLAIMANT および IDX_MBR_IDENTFN) とその順序を特定すると役立ちます。

日付タイプの問題だと思います。たとえば、mbr_identfn.identfd_type がインデックスの先頭の列で数値の場合、:identfd_type_1 が文字変数 (またはその逆) の場合、使用できなくなります。ただし、型が少ない場合は、スキップ スキャンでインデックスを使用できます。

また、where 句と join 句の両方で述語 "mbr.mbr_idn = mbr_identfn.mbr_idn" を指定します。

于 2010-05-05T23:10:43.277 に答える