Почему Oracle использует Skip Scan для этого запроса?

StackOverflow https://stackoverflow.com/questions/2776884

  •  03-10-2019
  •  | 
  •  

Вопрос

Вот вывод 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))

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

На основании моего чтения Oracle's Документация сканирования Skip, Skip Scan наиболее полезен, когда первый столбец индекса имеет низкое количество уникальных значений. Дело в том, что первый индекс этого столбца имеет большое количество университетов. Итак, я правиюсь, предположив, что пропустить сканирование не то, что нужно сделать здесь? Кроме того, какое сканирование должен Это делать? Должен ли я сделать еще несколько намекать к этому запросу?

РЕДАКТИРОВАТЬ: Я должен также отметить, что предложение Query's Wify использует столбцы в IDX_MBR_IDENTFN и никаких столбцов, кроме того, что в этом индексе. Так что, насколько я могу сказать, я не пропускаю никаких столбцов.

Редактировать 2.: Я сделал несколько вещей, чтобы ускорить этот запрос. Прежде всего, я удалил пейджинг. Как получается, этот запрос возвращает только одну строку. Во-вторых, я добавил 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))
Это было полезно?

Решение

Индекс Skip Scan означает, что первый столбец индекса игнорируется. Эта производительность затрат, поскольку Oracle прочитал каждый элемент первого столбца и проверить, является ли вторым (или третьим, ...) столбцем, для чего вы искали. Обычно это быстрее, чем сканирование полной таблицы (зависит от вашего запроса), но медленнее Сканирование диапазона индекса.

Попробуйте создать отдельный индекс на столбце, который является частью IDX_MBR_IDENTFN и используется в вашем запросе.


Например, если your_table Похоже:

id  status
1   0
2   0
3   0
4   1

и у вас есть составный индекс на (id, status), Запрос Select * From your_table Where status = 1 может использовать индекс, но для того, чтобы найти правильные строки, он должен читать каждую строку индекса (ID 1 к 4) и проверь status.


Обновлять: Следующий индекс может немного улучшить производительность, но вам придется попробовать, если это действительно поможет:

mbr_identfn( identfd_type, identfd_number, entity_active, mbr_idn )

Это может даже помочь избежать намеки.

Другие советы

Я бы сдвинул мою сосредоточенность вдали от сканирования SKIP.

Снаппин TKPROF показывает, что ваш первый приоритет должен быть сократить количество раз вы выдаете это утверждение. В настоящее время вы выполняете это утверждение 9936 раз. И каждое исполнение занимает всего 405/9936 секунд. Разумно быстро. Но не если вы выполняете его 9936 раз.

Таким образом, это утверждение почти уверена в конструкции петли. В каждой итерации вы предоставляете другой набор входных параметров (: IdentFD_TYPE_1,: Identfd_number_1,: Entity_ Active_1,: Rownum_1,: ORA_RN_1). Перепишите эту контурную конструкцию, чтобы это оператор выполнить один раз для всего набора, и ваша проблема с производительностью, вероятно, будет вещь из прошлого. Если нет, пожалуйста, опубликуйте новый выход TKPROF.

С уважением, Роб.

Это поможет, если вы определили, какие столбцы в индексах (pk_claimant и idx_mbr_identfn) есть, и в каком порядке.

Я подозреваю, что это проблема dateType. Если, например, mbr_identfn.identfd_type - это ведущий столбец индекса и является числовым, но ваш: Identfd_type_1 - это переменная символа (или наоборот), она становится непригодным. Однако, если есть несколько типов, то индекс можно использовать с помощью Skip Scan.

Вы также указываете предикат «MBR.MBR_IDN = MBR_IDEDFN.MBR_IDN» в разделе «Где», так и в пункте «Присоединение».

Чтобы объяснить Skip Scan ... это, кажется, является соответствующей частью предиката запроса:

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, чтобы посмотреть в индексе; Это означает, что мы не можем сделать уникальный или диапазон сканирования. Но у нас есть значения (как переменные связывания) для других трех столбцов индекса, поэтому мы можем сделать Skip Scan. Oracle выбирает сделать это, чтобы вообще вообще доступа к базовой таблице, что кажется разумным.

Какой основной ключ MBR_IDREFN? Это только MBR_IDN?

Я думаю, что у вас должен быть отдельный индекс на MBR_IDDEFN с некоторыми или всеми идентификатором Idepd_type, Identfd_number и Entity_active в качестве ведущих столбцов. Это позволит допустить диапазон или уникальное сканирование, а не пропустить сканирование.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top