21

Es gibt neben partitionierten Tabelle:Oracle vorbereitete Anweisung hängt

CREATE TABLE "ERMB_LOG_TEST_BF"."OUT_SMS"(
    "TRX_ID" NUMBER(19,0) NOT NULL ENABLE, 
    "CREATE_TS" TIMESTAMP (3) DEFAULT systimestamp NOT NULL ENABLE, 
    /* other fields... */ 
) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
    STORAGE(BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) 
    TABLESPACE "ERMB_LOG_TEST_BF" 
    PARTITION BY RANGE ("TRX_ID") INTERVAL (281474976710656) 
    (PARTITION "SYS_P1358" VALUES LESS THAN (59109745109237760) SEGMENT CREATION IMMEDIATE 
    PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
    NOCOMPRESS LOGGING 
    STORAGE(INITIAL 8388608 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 
    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 
    BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) 
    TABLESPACE "ERMB_LOG_TEST_BF"); 

CREATE INDEX "ERMB_LOG_TEST_BF"."OUT_SMS_CREATE_TS_TRX_ID_IX" ON "ERMB_LOG_TEST_BF"."OUT_SMS" ("CREATE_TS" DESC, "TRX_ID" DESC) 
    PCTFREE 10 INITRANS 2 MAXTRANS 255 
    STORAGE(
    BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) LOCAL 
    (PARTITION "SYS_P1358" 
    PCTFREE 10 INITRANS 2 MAXTRANS 255 LOGGING 
    STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 
    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 
    BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) 
    TABLESPACE "ERMB_LOG_TEST_BF"); 

Ich habe SQL-Abfrage, die nach Datum und Transaktion bestellt 20 Datensätze auswählen:

:

select rd from (
    select /*+ INDEX(OUT_SMS OUT_SMS_CREATE_TS_TRX_ID_IX) */ rowid rd 
    from OUT_SMS  
    where TRX_ID between 34621422135410688 and 72339069014638591  
     and CREATE_TS between to_timestamp('2013-02-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss') 
         and to_timestamp('2013-03-06 08:57:00', 'yyyy-mm-dd hh24:mi:ss')  
    order by CREATE_TS DESC, TRX_ID DESC 
) where rownum <= 20 

Oracle nächsten Plan generiert hat

----------------------------------------------------------------------------------------------------------------------------------- 
    | Id | Operation     | Name      | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | Pstart| Pstop | 
    ----------------------------------------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT   |        | 20 | 240 |  | 4788K (1)| 00:05:02 |  |  | 
    |* 1 | COUNT STOPKEY    |        |  |  |  |   |   |  |  | 
    | 2 | VIEW      |        | 312M| 3576M|  | 4788K (1)| 00:05:02 |  |  | 
    |* 3 | SORT ORDER BY STOPKEY |        | 312M|  9G| 12G| 4788K (1)| 00:05:02 |  |  | 
    | 4 |  PARTITION RANGE ITERATOR|        | 312M|  9G|  | 19 (0)| 00:00:01 |  1 | 48 | 
    |* 5 |  COUNT STOPKEY   |        |  |  |  |   |   |  |  | 
    |* 6 |  INDEX RANGE SCAN  | OUT_SMS_CREATE_TS_TRX_ID_IX | 312M|  9G|  | 19 (0)| 00:00:01 |  1 | 48 | 
    ----------------------------------------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

    1 - filter(ROWNUM<=20) 
    3 - filter(ROWNUM<=20) 
    5 - filter(ROWNUM<=20) 
    6 - access(SYS_OP_DESCEND("CREATE_TS")>=HEXTORAW('878EFCF9F6C5FEFAFF') AND 
    SYS_OP_DESCEND("TRX_ID")>=HEXTORAW('36F7E7D7F8A4F0BFA9A3FF') AND 
    SYS_OP_DESCEND("CREATE_TS")<=HEXTORAW('878EFDFEF8FEF8FF') AND 
    SYS_OP_DESCEND("TRX_ID")<=HEXTORAW('36FBD0E9D4E9DBD5F8A6FF')) 
    filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("CREATE_TS"))<=TIMESTAMP' 2013-03-06 08:57:00,000000000' AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("TRX_ID"))<=72339069014638591 AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("TRX_ID"))>=34621422135410688 AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("CREATE_TS"))>=TIMESTAMP' 2013-02-01 00:00:00,000000000') 

Es funktioniert perfekt.

By the way, Tisch OUT_SMS ist durch TRX_ID Feld partitioniert und OUT_SMS_CREATE_TS_TRX_ID_IX ist lokaler Index(CREATE_TS DESC, TRX_ID DESC) auf jeder Partition.

Aber wenn ich konvertieren, diese Abfrage vorbereitete Anweisung:

select rd from (
    select /*+ INDEX(OUT_SMS OUT_SMS_CREATE_TS_TRX_ID_IX) */ rowid rd 
    from OUT_SMS  
    where TRX_ID between ? and ?  
     and CREATE_TS between ? and ? 
    order by CREATE_TS DESC, TRX_ID DESC 
) where rownum <= 20 

Oracle erzeugt nächsten Plan:

---------------------------------------------------------------------------------------------------------------------------- 
    | Id | Operation     | Name      | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
    ---------------------------------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT    |        | 20 | 240 | 14743 (1)| 00:00:01 |  |  | 
    |* 1 | COUNT STOPKEY    |        |  |  |   |   |  |  | 
    | 2 | VIEW      |        | 1964 | 23568 | 14743 (1)| 00:00:01 |  |  | 
    |* 3 | SORT ORDER BY STOPKEY  |        | 1964 | 66776 | 14743 (1)| 00:00:01 |  |  | 
    |* 4 |  FILTER     |        |  |  |   |   |  |  | 
    | 5 |  PARTITION RANGE ITERATOR|        | 1964 | 66776 | 14742 (1)| 00:00:01 | KEY | KEY | 
    |* 6 |  INDEX RANGE SCAN  | OUT_SMS_CREATE_TS_TRX_ID_IX | 1964 | 66776 | 14742 (1)| 00:00:01 | KEY | KEY | 
    ---------------------------------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

    1 - filter(ROWNUM<=20) 
    3 - filter(ROWNUM<=20) 
    4 - filter(TO_TIMESTAMP(:RR,'yyyy-mm-dd hh24:mi:ss')<=TO_TIMESTAMP(:T,'yyyy-mm-dd hh24:mi:ss') AND 
    TO_NUMBER(:ABC)<=TO_NUMBER(:EBC)) 
    6 - access(SYS_OP_DESCEND("CREATE_TS")>=SYS_OP_DESCEND(TO_TIMESTAMP(:T,'yyyy-mm-dd hh24:mi:ss')) AND 
    SYS_OP_DESCEND("TRX_ID")>=SYS_OP_DESCEND(TO_NUMBER(:EBC)) AND 
    SYS_OP_DESCEND("CREATE_TS")<=SYS_OP_DESCEND(TO_TIMESTAMP(:RR,'yyyy-mm-dd hh24:mi:ss')) AND 
    SYS_OP_DESCEND("TRX_ID")<=SYS_OP_DESCEND(TO_NUMBER(:ABC))) 
    filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("TRX_ID"))>=TO_NUMBER(:ABC) AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("TRX_ID"))<=TO_NUMBER(:EBC) AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("CREATE_TS"))>=TO_TIMESTAMP(:RR,'yyyy-mm-dd hh24:mi:ss') AND 
    SYS_OP_UNDESCEND(SYS_OP_DESCEND("CREATE_TS"))<=TO_TIMESTAMP(:T,'yyyy-mm-dd hh24:mi:ss')) 

Betrieb COUNT STOPKEY von Plan verschwindet. Diese Operation sollte nach dem Index analysiert werden, um 20 Zeilen von jeder Partition wie die erste Abfrage zu erhalten.

Wie kann ich eine vorbereitete Anweisung erstellen, um COUNT STOPKEY im Plan zu haben?

+0

Was ist die vorbereitete Anweisung und '?' zwischen ? und ?' ? Kann Ihnen nur einen Tipp zu Daten geben. Um Daten zu vergleichen, entfernen Sie den Zeitabschnitt mit trunc(), es sei denn, Sie müssen wirklich mit den Sekunden vergleichen. Verwenden Sie row_number() anstelle von rownum. – Art

+0

Welche Art von Partitionierung ist hier beteiligt? Am besten zeigen Sie die Tabelle und den Index DDL. Verwenden Sie DBMS_Xplan, um den Ausführungsplan zu erhalten und fügen Sie ihn in die Frage ein, anstatt mit Bildern zu verlinken. –

+0

"Soll die Abfrage nur Zeilen von einer einzelnen Partition abrufen?" - Nein. Ich muss Daten von mehreren Partitionen holen. –

Antwort

1

Ist Dynamic SQL eine Option? Auf diese Weise können Sie die Filterwerte TRX_ID und CREATE_TS "injizieren", wodurch die Verwendung von Bind-Variablen entfällt. Vielleicht würde der generierte Plan dann COUNT STOPKEY enthalten.

Mit Dynamic SQL bedeutete ich für Sie, das SQL dynamisch zu konstruieren und dann mit EXECUTE IMMEDIATE oder OPEN aufzurufen. Dadurch können Sie Ihre Filter direkt ohne Bindevariablen verwenden. Beispiel:

v_sql VARCHAR2(1000) := 
    'select rd from (
     select /*+ INDEX(OUT_SMS OUT_SMS_CREATE_TS_TRX_ID_IX) */ rowid rd 
     from OUT_SMS  
     where TRX_ID between ' || v_trx_id_min || ' and ' || v_trx_id_maxb || '  
      and CREATE_TS between ' || v_create_ts_min|| ' and ' || v_create_ts_max || ' 
     order by CREATE_TS DESC, TRX_ID DESC 
    ) where rownum <= 20'; 

dann rufen Sie es mit:

EXECUTE IMMEDIATE v_sql; 

oder sogar:

OPEN cursor_out FOR v_sql; 
+0

Bindungsvariablen emulieren ist für die Leistung nicht interessant. Entschuldigung, ich verstehe deine Frage nicht. –

+0

Bitte lesen Sie die bearbeitete Antwort. Die Idee ist nicht bind Variablen zu verwenden. Wie analysieren Sie den Tisch? –

9

Wenn Sie Bind-Variablen, ist Oracle gezwungen dynamic partition pruning statt static partition pruning zu verwenden. Dies hat zur Folge, dass Oracle zur Parserzeit nicht weiß, auf welche Partitionen zugegriffen wird, da sich dies aufgrund Ihrer Eingabevariablen ändert.

Das bedeutet, dass wir bei Verwendung von literalen Werten (anstelle von Bind-Variablen) wissen, auf welche Partitionen Ihr lokaler Index zugreifen wird. Daher kann die count stopkey auf den Ausgang des Index angewendet werden, bevor wir die Partitionen löschen.

Wenn Sie Bind-Variablen verwenden, muss partition range iterator herausfinden, auf welche Partitionen Sie zugreifen. Es hat dann eine Überprüfung, um sicherzustellen, dass die erste Ihrer Variablen in den Zwischenoperationen tatsächlich einen niedrigeren Wert als die zweite (die Operation filter im zweiten Plan) hat.

Dies kann leicht reproduziert werden, wie der folgende Testfall zeigt:

create table tab (
    x date, 
    y integer, 
    filler varchar2(100) 
) partition by range(x) (
    partition p1 values less than (date'2013-01-01'), 
    partition p2 values less than (date'2013-02-01'), 
    partition p3 values less than (date'2013-03-01'), 
    partition p4 values less than (date'2013-04-01'), 
    partition p5 values less than (date'2013-05-01'), 
    partition p6 values less than (date'2013-06-01') 
); 


insert into tab (x, y) 
    select add_months(trunc(sysdate, 'y'), mod(rownum, 5)), rownum, dbms_random.string('x', 50) 
    from dual 
    connect by level <= 1000; 

create index i on tab(x desc, y desc) local; 

exec dbms_stats.gather_table_stats(user, 'tab', cascade => true); 

explain plan for 
SELECT * FROM (
    SELECT rowid FROM tab 
    where x between date'2013-01-01' and date'2013-02-02' 
    and y between 50 and 100 
    order by x desc, y desc 
) 
where rownum <= 5; 

SELECT * FROM table(dbms_xplan.display(null, null, 'BASIC +ROWS +PARTITION')); 

--------------------------------------------------------------------                                                           
| Id | Operation     | Name | Rows | Pstart| Pstop |                                                           
--------------------------------------------------------------------                                                           
| 0 | SELECT STATEMENT   |  |  1 |  |  |                                                           
| 1 | COUNT STOPKEY    |  |  |  |  |                                                           
| 2 | VIEW      |  |  1 |  |  |                                                           
| 3 | SORT ORDER BY STOPKEY |  |  1 |  |  |                                                           
| 4 |  PARTITION RANGE ITERATOR|  |  1 |  2 |  3 |                                                           
| 5 |  COUNT STOPKEY   |  |  |  |  |                                                           
| 6 |  INDEX RANGE SCAN  | I |  1 |  2 |  3 |                                                           
-------------------------------------------------------------------- 

explain plan for 
SELECT * FROM (
    SELECT rowid FROM tab 
    where x between to_date(:st, 'dd/mm/yyyy') and to_date(:en, 'dd/mm/yyyy') 
    and y between :a and :b 
    order by x desc, y desc 
) 
where rownum <= 5; 

SELECT * FROM table(dbms_xplan.display(null, null, 'BASIC +ROWS +PARTITION')); 

---------------------------------------------------------------------                                                           
| Id | Operation     | Name | Rows | Pstart| Pstop |                                                           
---------------------------------------------------------------------                                                           
| 0 | SELECT STATEMENT    |  |  1 |  |  |                                                           
| 1 | COUNT STOPKEY    |  |  |  |  |                                                           
| 2 | VIEW      |  |  1 |  |  |                                                           
| 3 | SORT ORDER BY STOPKEY  |  |  1 |  |  |                                                           
| 4 |  FILTER     |  |  |  |  |                                                           
| 5 |  PARTITION RANGE ITERATOR|  |  1 | KEY | KEY |                                                           
| 6 |  INDEX RANGE SCAN  | I |  1 | KEY | KEY |                                                           
--------------------------------------------------------------------- 

Wie in Ihrem Beispiel kann die zweite Abfrage nur die Partitionen zu einem key zur Analysezeit filtert, anstatt die genauen Partitionen im ersten Beispiel.

Dies ist einer der seltenen Fälle, in denen literale Werte eine bessere Leistung bieten als bind-Variablen. Sie sollten untersuchen, ob dies eine Möglichkeit für Sie ist.

Schließlich sagen Sie, Sie wollen 20 Zeilen von jeder Partition. Ihre Anfrage als Stand wird dies nicht tun, es werden nur die ersten 20 Zeilen nach Ihrer Bestellung zurückgegeben. Für 20 Zeilen/Partition, müssen Sie so etwas wie dies tun:

select rd from (
    select rowid rd, 
      row_number() over (partition by trx_id order by create_ts desc) rn 
    from OUT_SMS  
    where TRX_ID between ? and ?  
     and CREATE_TS between ? and ? 
    order by CREATE_TS DESC, TRX_ID DESC 
) where rn <= 20 

UPDATE

Der Grund Sie nicht die count stopkey bekommen ist mit dem filter Betrieb in Zeile 4 der "zu tun schlechter "Plan. Sie können dies deutlicher sehen, wenn Sie das obige Beispiel wiederholen, aber keine Partitionierung vornehmen.

Dies gibt Ihnen die folgenden Pläne:

----------------------------------------                                                                  
| Id | Operation    | Name |                                                                  
----------------------------------------                                                                  
| 0 | SELECT STATEMENT  |  |                                                                  
|* 1 | COUNT STOPKEY   |  |                                                                  
| 2 | VIEW     |  |                                                                  
|* 3 | SORT ORDER BY STOPKEY|  |                                                                  
|* 4 |  TABLE ACCESS FULL | TAB |                                                                  
----------------------------------------                                                                  

Predicate Information (identified by operation id):                                                               
---------------------------------------------------                                                               

    1 - filter(ROWNUM<=5)                                                                      
    3 - filter(ROWNUM<=5)                                                                      
    4 - filter("X">=TO_DATE(' 2013-01-01 00:00:00', 'syyyy-mm-dd                                                            
       hh24:mi:ss') AND "X"<=TO_DATE(' 2013-02-02 00:00:00', 'syyyy-mm-dd                                                        
       hh24:mi:ss') AND "Y">=50 AND "Y"<=100)                                                                                                                                  

----------------------------------------                                                                  
| Id | Operation    | Name |                                                                  
----------------------------------------                                                                  
| 0 | SELECT STATEMENT  |  |                                                                  
|* 1 | COUNT STOPKEY   |  |                                                                  
| 2 | VIEW     |  |                                                                  
|* 3 | SORT ORDER BY STOPKEY|  |                                                                  
|* 4 |  FILTER    |  |                                                                  
|* 5 |  TABLE ACCESS FULL | TAB |                                                                  
----------------------------------------                                                                  

Predicate Information (identified by operation id):                                                               
---------------------------------------------------                                                               

    1 - filter(ROWNUM<=5)                                                                      
    3 - filter(ROWNUM<=5)                                                                      
    4 - filter(TO_NUMBER(:A)<=TO_NUMBER(:B) AND                                                                
       TO_DATE(:ST,'dd/mm/yyyy')<=TO_DATE(:EN,'dd/mm/yyyy'))                                                           
    5 - filter("Y">=TO_NUMBER(:A) AND "Y"<=TO_NUMBER(:B) AND                                                             
       "X">=TO_DATE(:ST,'dd/mm/yyyy') AND "X"<=TO_DATE(:EN,'dd/mm/yyyy')) 

Wie Sie sehen können, gibt es einen zusätzlichen filter Betrieb, wenn Sie vor den sort order by stopkey erscheinen Bind-Variablen verwenden. Dies geschieht nach dem Zugriff auf den Index. Dadurch wird überprüft, ob die Werte für die Variablen die Rückgabe von Daten zulassen (die erste Variable in Ihrer Zwischendatei hat tatsächlich einen niedrigeren Wert als die zweite). Dies ist nicht erforderlich, wenn Literale verwendet werden, da der Optimierer bereits weiß, dass 50 kleiner als 100 ist (in diesem Fall). Es ist nicht bekannt, ob: a weniger als: b zur Analysezeit ist.

Warum genau das ist, weiß ich nicht. Es könnte absichtliches Design von Oracle sein - es macht keinen Sinn, die Stopkey-Prüfung durchzuführen, wenn die für die Variablen festgelegten Werte zu null Zeilen führen - oder nur ein Versehen.

+3

Der Unterschied ist, dass im ersten Fall die COUNT STOPKEY ** innerhalb ** der Partition Range Index Scan geschoben wird, was bedeutet, dass Oracle intelligent genug ist zu erkennen, dass es nicht mehr als 20 Zeilen benötigt, um 20 Zeilen zu erhalten 20 Zeilen pro Partition. Diese Subtilität geht verloren, wenn Bindevariablen verwendet werden. Ich verstehe nicht, warum die dynamische Partitionierung nicht verbessert werden konnte, um das rownum-Prädikat bei jedem Partitionsscan zu pushen. Sie geben nicht wirklich einen triftigen Grund für dieses Verhalten, noch eine gute Problemumgehung =) –

+0

@VincentMalgrat - nun, ich würde argumentieren, dass "nicht Bind Variablen verwenden" ist eine Umgehungslösung, obwohl nicht unbedingt wünschenswert! Wenn die Abfrage selten ausgeführt wird und/oder eine begrenzte Anzahl von Werten im Filter für "trx_id" verwendet wird, sollte der zusätzliche Parsing-Aufwand minimal sein, was dies zu einem möglichen Ansatz macht. –

+0

Ich stimme Ihrer Problemumgehung zu, leider scheint das OP Bind-Variablen verwenden zu wollen. Ich stimme Ihrer Erklärung jedoch nicht zu =) Das 'FILTER' ersetzt nicht das 'COUNT STOPKEY', da es sich um grundlegend unterschiedliche Operationen handelt. Wie du erklärst, ermöglicht es der "Filter" Oracle, unmögliche Operationen zu verzweigen. Der "COUNT STOPKEY" ist eine Optimierung, die es Oracle ermöglicht, nur 20 Zeilen pro Partition abzurufen, da insgesamt 20 Zeilen benötigt werden. Bei der Verwendung von Bind-Variablen fehlt diese Optimierung, dies ist wahrscheinlich ein Fehler (siehe verwandte Metalink-Fehler 12873183 und 8500130). –

7

Ich kann Ihre Ergebnisse auf 11.2.0.3 reproduzieren. Hier ist mein Testfall:

SQL> -- Table with 100 partitions of 100 rows 
SQL> CREATE TABLE out_sms 
    2 PARTITION BY RANGE (trx_id) 
    3  INTERVAL (100) (PARTITION p0 VALUES LESS THAN (0)) 
    4 AS 
    5 SELECT ROWNUM trx_id, 
    6   trunc(SYSDATE) + MOD(ROWNUM, 50) create_ts 
    7 FROM dual CONNECT BY LEVEL <= 10000; 

Table created 

SQL> CREATE INDEX OUT_SMS_IDX ON out_sms (create_ts desc, trx_id desc) LOCAL; 

Index created 

[static plan] 

SELECT rd 
    FROM (SELECT /*+ INDEX(OUT_SMS OUT_SMS_IDX) */ 
     rowid rd 
      FROM out_sms 
     WHERE create_ts BETWEEN systimestamp AND systimestamp + 10 
      AND trx_id BETWEEN 1 AND 500 
     ORDER BY create_ts DESC, trx_id DESC) 
WHERE rownum <= 20;  
--------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Pstart| Pstop | 
--------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |    |  1 |  |  | 
|* 1 | COUNT STOPKEY    |    |  |  |  | 
| 2 | VIEW      |    |  1 |  |  | 
|* 3 | SORT ORDER BY STOPKEY |    |  1 |  |  | 
| 4 |  PARTITION RANGE ITERATOR|    |  1 |  2 |  7 | 
|* 5 |  COUNT STOPKEY   |    |  |  |  | 
|* 6 |  INDEX RANGE SCAN  | OUT_SMS_IDX |  1 |  2 |  7 | 
--------------------------------------------------------------------------- 

[dynamic]  
---------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Pstart| Pstop | 
---------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |    |  1 |  |  | 
|* 1 | COUNT STOPKEY    |    |  |  |  | 
| 2 | VIEW      |    |  1 |  |  | 
|* 3 | SORT ORDER BY STOPKEY  |    |  1 |  |  | 
|* 4 |  FILTER     |    |  |  |  | 
| 5 |  PARTITION RANGE ITERATOR|    |  1 | KEY | KEY | 
|* 6 |  INDEX RANGE SCAN  | OUT_SMS_IDX |  1 | KEY | KEY | 
---------------------------------------------------------------------------- 

Wie in Ihrem Beispiel des ROWNUM Prädikat innerhalb die Partition Indexbereich Scan im ersten Fall gedrückt wird, nicht im zweiten Fall. Wenn Sie statische Variablen verwenden, zeigt der Plan, dass Oracle nur 20 Zeilen pro Partition abruft, während Oracle bei Verwendung dynamischer Variablen alle Zeilen abruft, die die WHERE-Klausel in jeder Partition erfüllen. Ich konnte eine Einstellung oder eine Statistikkonfiguration nicht finden, in der das Prädikat gedrückt werden könnte, wenn Bindevariablen verwendet werden.

Ich hoffte, dass Sie dynamische Filter mit größeren statischen Grenzen verwenden könnten das System zum Spiel, aber es scheint, dass das ROWNUM Prädikat nicht als innerhalb einzelner Partitionen verwendet wird, sobald es dynamische Variablen vorhanden:

SELECT rd 
    FROM (SELECT /*+ INDEX(OUT_SMS OUT_SMS_IDX) */ 
     rowid rd 
      FROM out_sms 
     WHERE nvl(create_ts+:5, sysdate) BETWEEN :1 AND :2 
      AND nvl(trx_id+:6, 0) BETWEEN :3 AND :4 
      AND trx_id BETWEEN 1 AND 500 
      AND create_ts BETWEEN systimestamp AND systimestamp + 10 
     ORDER BY create_ts DESC, trx_id DESC) 
WHERE rownum <= 20 

Plan hash value: 2740263591 

---------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Pstart| Pstop | 
---------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |    |  1 |  |  | 
|* 1 | COUNT STOPKEY    |    |  |  |  | 
| 2 | VIEW      |    |  1 |  |  | 
|* 3 | SORT ORDER BY STOPKEY  |    |  1 |  |  | 
|* 4 |  FILTER     |    |  |  |  | 
| 5 |  PARTITION RANGE ITERATOR|    |  1 |  2 |  7 | 
|* 6 |  INDEX RANGE SCAN  | OUT_SMS_IDX |  1 |  2 |  7 | 
---------------------------------------------------------------------------- 

Wenn diese Abfrage wichtig ist und ihre Leistung kritisch ist, können Sie den Index in einen globalen Index umwandeln. Es wird die Partitionswartung erhöhen, aber die meisten Partitionsoperationen können online mit aktuellen Oracle-Versionen verwendet werden.Ein globaler Index funktioniert in diesem Fall wie bei einer nicht partitionierten Standardtabelle:

SQL> drop index out_sms_idx; 

Index dropped 

SQL> CREATE INDEX OUT_SMS_IDX ON out_sms (create_ts DESC, trx_id desc); 

Index created 

SELECT rd 
    FROM (SELECT 
     rowid rd 
      FROM out_sms 
     WHERE create_ts BETWEEN :1 AND :2 
      AND trx_id BETWEEN :3 AND :4 
     ORDER BY create_ts DESC, trx_id DESC) 
WHERE rownum <= 20 

------------------------------------------------------------------------ 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| 
------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT |    |  1 | 12 |  2 (0)| 
|* 1 | COUNT STOPKEY  |    |  |  |   | 
| 2 | VIEW    |    |  1 | 12 |  2 (0)| 
|* 3 | FILTER   |    |  |  |   | 
|* 4 |  INDEX RANGE SCAN| OUT_SMS_IDX |  1 | 34 |  2 (0)| 
------------------------------------------------------------------------ 
+0

Ich bekomme Ihre Idee über den globalen Index, aber die Anzahl der Zeilen könnte sehr groß sein und ich werde Partitionsvorteile verlieren. –

Verwandte Themen