2016-10-18 3 views
6

После миграции с Oracle 11g на 12c мы имеем массовую проблему с производительностью, например, со следующим оператором select. То же самое утверждение отлично работает в 11g.Подзапрос в Oracle 12c с плохой производительностью

Таблица и индексы

-- Create table 
create table PS_CS_ADRART_TB 
(
    cs_adressmandant VARCHAR2(5) not null, 
    cs_person_id  VARCHAR2(20) not null, 
    cs_beziehung_id VARCHAR2(20) not null, 
    seqnum    INTEGER not null, 
    effdt    DATE, 
    eff_status   VARCHAR2(1) not null, 
    cs_adrart_cd  VARCHAR2(20) not null, 
    cs_adress_id  VARCHAR2(20) not null, 
    cs_kdnr_as400  VARCHAR2(8) not null, 
    cs_plzgk   VARCHAR2(11) not null, 
    cs_plz_pf   VARCHAR2(15) not null, 
    cs_aendgr_cd  VARCHAR2(20) not null, 
    cs_datasource_cd VARCHAR2(20) not null, 
    cs_betrag   NUMBER(14,4) not null, 
    cs_belegdat  DATE, 
    cs_adrtyp_xl  VARCHAR2(2) not null, 
    cs_checked   VARCHAR2(1) not null, 
    cs_journal_xl  VARCHAR2(4) not null, 
    address2   VARCHAR2(55) not null, 
    row_added_dttm  TIMESTAMP(6), 
    row_added_oprid VARCHAR2(30) not null, 
    row_lastmant_dttm TIMESTAMP(6), 
    row_lastmant_oprid VARCHAR2(30) not null, 
    cs_recstat_xl  VARCHAR2(4) not null, 
    cs_update_count NUMBER(10) not null 
) 
tablespace CS_APP 
    pctfree 10 
    initrans 1 
    maxtrans 255 
    storage 
    (
    initial 102416K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
-- Create/Recreate indexes 
create unique index PSACS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESSMANDANT, CS_KDNR_AS400, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PSBCS_ADRART_TB on PS_CS_ADRART_TB (CS_PERSON_ID) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PSCCS_ADRART_TB on PS_CS_ADRART_TB (CS_BEZIEHUNG_ID) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create unique index PS_CS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PSDCS_ADRART_TB on PS_CS_ADRART_TB (CS_PLZ_PF) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS0CS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESS_ID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS1CS_ADRART_TB on PS_CS_ADRART_TB (CS_KDNR_AS400, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 20M 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS2CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS3CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS4CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS5CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 
create index PS6CS_ADRART_TB on PS_CS_ADRART_TB (CS_RECSTAT_XL, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) 
    tablespace PSINDEX 
    pctfree 10 
    initrans 2 
    maxtrans 255 
    storage 
    (
    initial 40K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 

Размер таблицы

select count(*) from ps_cs_adrart_tb a 
--> 41367270 

Оператор Select

SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD') 
     from PS_CS_ADRART_TB A 
    where A.CS_ADRESSMANDANT = '001' 
     and a.cs_kdnr_as400 = '63916917' 
     and a.effdt = 
      (select max(b.effdt) 
       from ps_cs_adrart_tb b 
      where b.cs_adressmandant = a.cs_adressmandant 
       and b.cs_person_id = a.cs_person_id 
       and b.cs_beziehung_id = a.cs_beziehung_id 
       and b.seqnum = a.seqnum 
       and b.effdt <= trunc(sysdate) 
       ) 

Объяснить план Oracle 11g

-------------------------------------------------------------------------------------------- 
    | Id | Operation      | Name   | Rows | Bytes | Cost | Time  | 
    -------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT    |     | 1 | 89 | 2 | 00:00:01 | 
    | * 1 | FILTER      |     |  |  |  |   | 
    | 2 | TABLE ACCESS BY INDEX ROWID | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 | 
    | * 3 |  INDEX RANGE SCAN   | PSACS_ADRART_TB | 1 |  | 1 | 00:00:01 | 
    | 4 | SORT AGGREGATE    |     | 1 | 59 |  |   | 
    | 5 |  FIRST ROW     |     | 1 | 59 | 1 | 00:00:01 | 
    | * 6 |  INDEX RANGE SCAN (MIN/MAX) | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 | 
    -------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    ------------------------------------------ 
    * 1 - filter("A"."EFFDT"= (SELECT MAX("B"."EFFDT") FROM "PS_CS_ADRART_TB" "B" WHERE "B"."EFFDT"<=TRUNC([email protected]!) AND "B"."SEQNUM"=:B1 AND "B"."CS_BEZIEHUNG_ID"=:B2 AND "B"."CS_PERSON_ID"=:B3 AND 
     "B"."CS_ADRESSMANDANT"=:B4)) 
    * 3 - access("A"."CS_ADRESSMANDANT"='001' AND "A"."CS_KDNR_AS400"='63916917') 
    * 6 - access("B"."CS_ADRESSMANDANT"=:B1 AND "B"."CS_PERSON_ID"=:B2 AND "B"."CS_BEZIEHUNG_ID"=:B3 AND "B"."SEQNUM"=:B4 AND "B"."EFFDT"<=TRUNC([email protected]!))  

Объяснить план Oracle 12c

------------------------------------------------------------------------------------------------------------ 
    | Id | Operation        | Name   | Rows  | Bytes  | Cost | Time  | 
    ------------------------------------------------------------------------------------------------------------ 
    | 0 | SELECT STATEMENT      |     |  1 |  140 | 34366 | 00:00:02 | 
    | * 1 | HASH JOIN       |     |  1 |  140 | 34366 | 00:00:02 | 
    | 2 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB |  1 |  89 |  1 | 00:00:01 | 
    | * 3 |  INDEX RANGE SCAN     | PS1CS_ADRART_TB |  1 |   |  1 | 00:00:01 | 
    | 4 | VIEW        | VW_SQ_1   | 41889 | 2136339 | 34365 | 00:00:02 | 
    | * 5 |  FILTER        |     |   |   |  |   | 
    | 6 |  HASH GROUP BY      |     | 41889 | 2471451 | 34365 | 00:00:02 | 
    | * 7 |  INDEX RANGE SCAN     | PS_CS_ADRART_TB | 12746381 | 752036479 | 34365 | 00:00:02 | 
    ------------------------------------------------------------------------------------------------------------ 

    Predicate Information (identified by operation id): 
    ------------------------------------------ 
    * 1 - access("A"."EFFDT"="MAX(B.EFFDT)" AND "ITEM_1"="A"."CS_ADRESSMANDANT" AND "ITEM_2"="A"."CS_PERSON_ID" AND "ITEM_3"="A"."CS_BEZIEHUNG_ID" AND "ITEM_4"="A"."SEQNUM") 
    * 3 - access("A"."CS_KDNR_AS400"='63916917' AND "A"."CS_ADRESSMANDANT"='001') 
    * 5 - filter('001'='001') 
    * 7 - access("B"."CS_ADRESSMANDANT"='001' AND "B"."EFFDT"<=TRUNC([email protected]!)) 
    * 7 - filter("B"."EFFDT"<=TRUNC([email protected]!)) 


    Note 
    ----- 
    - dynamic sampling used for this statement     

Как вы можете видеть, из индекса PS_CS_ADRART_TB только CS_ADRESSMANDANT и EFFDT используются для фильтрации данных, и это очень плохо ,

С помощью следующих несколько разных операторов выбора индекс используется Oracle 12c, как ожидается, для определения данных подзапроса.

Изменения, где условие наружного выбора

SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD') 
    from PS_CS_ADRART_TB A 
    where a.cs_kdnr_as400 = '53916917' -- without CS_ADRESSMANDANT condition 
    and a.effdt = 
     (select max(b.effdt) 
      from ps_cs_adrart_tb b 
      where b.cs_adressmandant = a.cs_adressmandant and 
       b.cs_person_id = a.cs_person_id 
      and b.cs_beziehung_id = a.cs_beziehung_id 
      and b.seqnum = a.seqnum 
      and b.effdt <= trunc(sysdate) 
      ) 

    -------------------------------------------------------------------------------------------------- 
    | Id | Operation        | Name   | Rows | Bytes | Cost | Time  | 
    -------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT      |     | 1 | 89 | 2 | 00:00:01 | 
    | 1 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 | 
    | * 2 | INDEX RANGE SCAN     | PS1CS_ADRART_TB | 1 |  | 1 | 00:00:01 | 
    | 3 |  SORT AGGREGATE     |     | 1 | 59 |  |   | 
    | 4 |  FIRST ROW      |     | 1 | 59 | 1 | 00:00:01 | 
    | * 5 |  INDEX RANGE SCAN (MIN/MAX)  | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 | 
    -------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    ------------------------------------------ 
    * 2 - access("A"."CS_KDNR_AS400"='53916917') 
    * 2 - filter("A"."EFFDT"= (SELECT MAX("B"."EFFDT") FROM "PS_CS_ADRART_TB" "B" WHERE "B"."EFFDT"<=TRUNC([email protected]!) AND "B"."SEQNUM"=:B1 AND "B"."CS_BEZIEHUNG_ID"=:B2 AND "B"."CS_PERSON_ID"=:B3 AND 
     "B"."CS_ADRESSMANDANT"=:B4)) 
    * 5 - access("B"."CS_ADRESSMANDANT"=:B1 AND "B"."CS_PERSON_ID"=:B2 AND "B"."CS_BEZIEHUNG_ID"=:B3 AND "B"."SEQNUM"=:B4 AND "B"."EFFDT"<=TRUNC([email protected]!)) 


    Note 
    ----- 
    - dynamic sampling used for this statement 

Использование min() вместо max() в подвыборках

SELECT A.CS_ADRESS_ID, A.SEQNUM, TO_CHAR(A.EFFDT, 'YYYY-MM-DD'), a.cs_person_id 
     from PS_CS_ADRART_TB A 
    where a.cs_kdnr_as400 = '63916917' and a.cs_adressmandant = '001' 
     and a.effdt = 
      (select min(b.effdt) 
       from ps_cs_adrart_tb b 
      where b.cs_adressmandant = a.cs_adressmandant 
       and b.cs_person_id = a.cs_person_id 
       and b.cs_beziehung_id = a.cs_beziehung_id 
       and b.seqnum = a.seqnum 
       and b.effdt <= sysdate 
       ); 

    --------------------------------------------------------------------------------------------------- 
    | Id | Operation        | Name   | Rows | Bytes | Cost | Time  | 
    --------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT      |     | 1 | 109 | 2 | 00:00:01 | 
    | 1 | NESTED LOOPS       |     | 1 | 109 | 2 | 00:00:01 | 
    | 2 | TABLE ACCESS BY INDEX ROWID BATCHED | PS_CS_ADRART_TB | 1 | 89 | 1 | 00:00:01 | 
    | * 3 |  INDEX RANGE SCAN     | PS1CS_ADRART_TB | 1 |  | 1 | 00:00:01 | 
    | * 4 | VIEW PUSHED PREDICATE    | VW_SQ_1   | 1 | 20 | 1 | 00:00:01 | 
    | 5 |  SORT GROUP BY      |     | 1 | 59 | 1 | 00:00:01 | 
    | * 6 |  INDEX RANGE SCAN     | PS_CS_ADRART_TB | 1 | 59 | 1 | 00:00:01 | 
    --------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    ------------------------------------------ 
    * 3 - access("A"."CS_KDNR_AS400"='63916917' AND "A"."CS_ADRESSMANDANT"='001') 
    * 4 - filter("A"."EFFDT"="MIN(B.EFFDT)" AND "ITEM_1"="A"."CS_ADRESSMANDANT") 
    * 6 - access("B"."CS_ADRESSMANDANT"='001' AND "B"."CS_PERSON_ID"="A"."CS_PERSON_ID" AND "B"."CS_BEZIEHUNG_ID"="A"."CS_BEZIEHUNG_ID" AND "B"."SEQNUM"="A"."SEQNUM" AND "B"."EFFDT"<[email protected]!) 


    Note 
    ----- 
    - dynamic sampling used for this statement 

Перестройка индексов (как описано выше) и обновление статистики по

EXEC DBMS_STATS.delete_table_stats('SYSADM', 'PS_CS_ADRART_TB'); 
-- EXEC DBMS_STATS.gather_table_stats('SYSADM', 'PS_CS_ADRART_TB'); 
EXEC DBMS_STATS.gather_table_stats(ownname => 'SYSADM', tabname => 'PS_CS_ADRART_TB', cascade => true); 

не помогает.

Что здесь происходит? Любые советы высоко ценятся. Заранее спасибо.

+1

Пожалуйста, дайте мне знать, почему вы вниз голосовать вопрос. –

+2

Вопрос ясен, хорошо помечен, содержит все инструкции для построения необходимых структур, дает информацию о размере таблиц, содержит много попыток и план для каждого утверждения ... Я не могу понять, что еще должно сделать этот вид вопроса ... +1 – Aleksej

+1

«* динамическая выборка, используемая для этого оператора * в плане 12c, как представляется, указывает на то, что статистика не является актуальной, несмотря на' gather_table_stats() '. Это изменяется, если вы звоните 'gather_table_stats()' с 'cascade => true'? (И чтобы исключить очевидное: SYSADM действительно является _owner_ этих таблиц?) –

ответ

1

Я думаю, вы должны установить параметр _unnest_subquery в FALSE.

Вы можете попробовать его с

alter session set "_unnest_subquery" = FALSE; 
+0

Вот и все! Поддержка Oracle рекомендует установить скрытый параметр _unnest_subquery в FALSE для повышения производительности приложений PeopleSoft в базах данных Oracle. Это то же поведение, что описано на http://www.bobbydurrettdba.com/2012/11/07/_unnest_subqueryfalse-for-peoplesoft-performance/ –

0

Frank. Насколько я понимаю, вы хотите получить одну последнюю строку любыми cs_person_id и cs_beziehung_id, где b.effdt ниже или равно, чем сегодня. Прежде всего, вы можете собрать статистику с информацией об избирательности. Это помогает CBO сделать выбор.

BEGIN 
dbms_stats.Gather_table_stats('SYSADM', 'PS_CS_ADRART_TB', 
method_opt => 'FOR COLUMNS SIZE 254 CS_KDNR_AS400 CS_ADRESSMANDANT'); 
END; 
/

Anothr один думаю, что я нахожу. У вас много индексов, которые могут быть созданы как уникальные. Он уже содержит уникальные столбцы индекса.

create unique index PS1CS_ADRART_TB on PS_CS_ADRART_TB (CS_KDNR_AS400, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT) ; 
create unique index PS0CS_ADRART_TB on PS_CS_ADRART_TB (CS_ADRESS_ID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS1CS_ADRART_TB on PS_CS_ADRART_TB (CS_KDNR_AS400, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS2CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS3CS_ADRART_TB on PS_CS_ADRART_TB (ROW_ADDED_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS4CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_DTTM, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS5CS_ADRART_TB on PS_CS_ADRART_TB (ROW_LASTMANT_OPRID, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 
create unique index PS6CS_ADRART_TB on PS_CS_ADRART_TB (CS_RECSTAT_XL, CS_ADRESSMANDANT, CS_PERSON_ID, CS_BEZIEHUNG_ID, SEQNUM, EFFDT); 

И в конце концов я пытаюсь переписать запрос, чтобы исключить подзапросов

SELECT cs_adress_id,seqnum, effdt_chr 
FROM (SELECT /* */ a.cs_adress_id 
      ,a.seqnum 
      ,to_char(a.effdt, 'YYYY-MM-DD') AS effdt_chr 
      ,dense_rank() over(PARTITION BY a.cs_adressmandant, a.cs_person_id 
             , a.cs_beziehung_id, a.seqnum 
           ORDER BY a.effdt DESC) AS rnk 
    FROM ps_cs_adrart_tb a 
    WHERE a.cs_adressmandant = '001' 
     AND a.cs_kdnr_as400 = '63916917' 
     AND a.effdt <= trunc(sysdate) 
) 
WHERE rnk = 1; 
+0

Да, оператор выбирает текущие данные для данного человека. Статистические значения в плане объяснения являются актуальными (12746381 строки для CS_ADRESSMANDANT = 001 и EFFDT = sysdate являются адекватным значением), поэтому возникает вопрос, почему дальнейшие поля CS_PERSON_ID, CS_BEZIEHUNG_ID и SEQNUM этого индекса не используются. И да, мы могли бы изменить инструкцию выбора или индексы, но мы стараемся минимизировать изменения нашего приложения, чтобы избежать усилий и риска побочных эффектов. –

+0

Тем не менее, я пробовал его с помощью 'method_opt => 'FOR COLUMNS SIZE 254 CS_KDNR_AS400 CS_ADRESSMANDANT'' - но безуспешно. –

+0

Его вид как проблема с hash_group_by. Не могли бы вы проверить запрос плана с подсказкой '/ * + opt_param ('_ optimizer_aggr_groupby_elim', 'true') * /'? –

Смежные вопросы