2016-10-18 46 views
6

Po migracji z Oracle 11g do 12c mamy ogromny problem z wydajnością, na przykład z następującą instrukcją select. To samo oświadczenie działa dobrze w 11g.Subselect w Oracle 12c ze złą wydajnością

Stół i Indeksy

-- 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 
); 

Tabela rozmiarów

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

Wybierz komunikat

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) 
       ) 

Wyjaśnij Plan 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]!))  

Objaśnienie 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     

Jak widać, z indeksu PS_CS_ADRART_TB tylko CS_ADRESSMANDANT i EFFDT są używane do filtrowania danych i to jest bardzo złe .

Przy następujących nieco różniących się stwierdzeniach, indeks jest używany przez Oracle 12c zgodnie z oczekiwaniami w celu określenia danych podselekcji.

Zmiana gdzie stan zewnętrznej wybrać

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 

Stosując min() zamiast max() w podselekcji

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 

Przebudowa indeksami (jak opisano powyżej) i aktualizacji statystyki według

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); 

nie pomaga.

Co się tutaj dzieje? Wszelkie porady są bardzo cenne. Z góry dziękuję.

+1

Proszę dać mi znać, dlaczego w dół głosować pytanie. –

+2

Pytanie jest jasne, dobrze oznakowane, zawiera wszystkie instrukcje do budowania potrzebnych struktur, podaje informacje o wielkości tabel, zawiera wiele prób i plan dla każdego wyciągu ... Nie mogę określić, co jeszcze powinien tego typu pytanie zawiera ... +1 – Aleksej

+1

"* Próbkowanie dynamiczne używane dla tego oświadczenia * w planie 12c wydaje się wskazywać, że statystyki nie są aktualne pomimo' gather_table_stats() '. Czy to się zmieni, jeśli zadzwonisz 'gather_table_stats()' z 'cascade => true'? (A żeby wykluczyć oczywiste: czy SYSADM jest rzeczywiście właścicielem tych tabel?) –

Odpowiedz

1

myślę, że należy ustawić parametr _unnest_subquery do FALSE.

Można spróbować z

alter session set "_unnest_subquery" = FALSE; 
+0

To wszystko! Pomoc techniczna firmy Oracle zaleca ustawienie parametru ukrytego parametru _unnest_subquery na wartość FALSE w celu poprawy wydajności aplikacji PeopleSoft w bazach danych Oracle. To samo zachowanie, które opisano na stronie http://www.bobbydurrettdba.com/2012/11/07/_unnest_subqueryfalse-for-peoplesoft-performance/ –

0

Frank. Jak rozumiem, chcesz uzyskać jeden ostatni wiersz przez cs_person_id i cs_beziehung_id, gdzie b.effdt niższe lub równe niż dzisiaj. Przede wszystkim możesz zebrać statystyki z informacjami o selektywności. Pomaga CBO dokonać wyboru.

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

Anothr jeden myślę, co znajduję. Masz wiele indeksów, które można stworzyć jako unikalne. Zawiera już unikalne kolumny indeksu.

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); 

I na koniec staram się przepisać zapytanie do wykluczenia podzapytaniu

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

Tak, instrukcja wybiera bieżące dane dla danej osoby. Wartości statystyczne w planie wyjaśniającym są aktualne (12746381 wierszy dla CS_ADRESSMANDANT = 001 i EFFDT = sysdate to odpowiednia wartość), dlatego pytanie, dlaczego dalsze pola CS_PERSON_ID, CS_BEZIEHUNG_ID i SEQNUM tego indeksu nie są używane. I tak, możemy zmienić oświadczenie lub indeksy wyboru, ale staramy się minimalizować zmiany w naszej aplikacji, aby uniknąć wysiłku i ryzyka związanego z efektami ubocznymi. –

+0

Mimo to próbowałem go z 'method_opt => 'FOR COLUMNS SIZE 254 CS_KDNR_AS400 CS_ADRESSMANDANT'' - ale bez powodzenia. –

+0

Wygląda jak problem z hash_group_by. Czy możesz sprawdzić zapytanie planowe z podpowiedzi '/ * + opt_param ('_ optimizer_aggr_groupby_elim', 'true') * /'? –