在Oracle 12c中进行子选择,性能不佳

从Oracle 11g迁移到12c之后,我们遇到了一个巨大的性能问题,例如使用以下select语句.同样的声明在11g中运行良好.

表和索引

-- 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,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,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,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,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,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,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,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 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(SYSDATE@!) 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(SYSDATE@!))

解释计划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(SYSDATE@!))
    * 7 - filter("B"."EFFDT"<=Trunc(SYSDATE@!))


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

如您所见,从索引PS_CS_ADRART_TB只有CS_ADRESSMANDANT和EFFDT用于过滤数据,这是非常糟糕的.

使用以下略有不同的select语句,Oracle 12c将按预期使用索引来确定subselect的数据.

改变外部选择的条件

SELECT  A.CS_ADRESS_ID,'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(SYSDATE@!) 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(SYSDATE@!))


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

在subselect中使用min()而不是max()

SELECT A.CS_ADRESS_ID,'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"<=SYSDATE@!)


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

没有帮助.

这里发生了什么?任何建议都非常感谢.提前致谢.

我认为你应该将参数_unnest_subquery设置为FALSE.

你可以尝试一下

alter session set "_unnest_subquery" = FALSE;

相关文章

Java Oracle 结果集是Java语言中处理数据库查询结果的一种方...
Java AES和Oracle AES是现代加密技术中最常使用的两种AES加密...
Java是一种广泛应用的编程语言,具备可靠性、安全性、跨平台...
随着移动互联网的发展,抽奖活动成为了营销活动中不可或缺的...
Java和Oracle都是在计算机领域应用非常广泛的技术,他们经常...
Java 是一门非常流行的编程语言,它可以运行于各种操作系统上...