Oracle
 sql >> Base de données >  >> RDS >> Oracle

STOPKEY manquant par partition dans le plan Oracle pour la pagination par index local

Lorsque vous utilisez des variables de liaison, Oracle est obligé d'utiliser élagage dynamique des partitions au lieu de élagage de partition statique . Le résultat est qu'Oracle ne sait pas au moment de l'analyse quelles partitions seront accessibles, car cela change en fonction de vos variables d'entrée.

Cela signifie que lorsque vous utilisez des valeurs littérales (au lieu de variables liées), nous savons à quelles partitions votre index local accédera. Par conséquent, la count stopkey peut être appliqué à la sortie de l'index avant d'élaguer les partitions.

Lors de l'utilisation de variables de liaison, l'partition range iterator doit déterminer à quelles partitions vous accédez. Il effectue ensuite une vérification pour s'assurer que la première de vos variables dans les opérations intermédiaires a effectivement une valeur inférieure à la seconde (le filter opération dans le second plan).

Cela peut facilement être reproduit, comme le montre le cas de test suivant :

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

Comme dans votre exemple, la deuxième requête ne peut filtrer les partitions que sur une key au moment de l'analyse, plutôt que les partitions exactes comme dans le premier exemple.

C'est l'un des rares cas où les valeurs littérales peuvent fournir de meilleures performances que les variables liées. Vous devriez étudier si c'est une possibilité pour vous.

Enfin, vous dites que vous voulez 20 lignes de chaque partition. Votre requête en l'état ne fera pas cela, elle vous renverra simplement les 20 premières lignes en fonction de votre commande. Pour 20 lignes/partition, vous devez faire quelque chose comme ceci :

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

MISE À JOUR

La raison pour laquelle vous n'obtenez pas la count stopkey concerne le filter opération en ligne 4 du "mauvais" plan. Vous pouvez voir cela plus clairement si vous répétez l'exemple ci-dessus, mais sans partitionnement.

Cela vous donne les plans suivants :

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

Comme vous pouvez le voir, il y a un filter supplémentaire opération lorsque vous utilisez des variables liées apparaissant avant le sort order by stopkey . Cela se produit après l'accès à l'index. Cela vérifie que les valeurs des variables permettront aux données d'être renvoyées (la première variable de votre entre a en fait une valeur inférieure à la seconde). Ce n'est pas nécessaire lors de l'utilisation de littéraux car l'optimiseur sait déjà que 50 est inférieur à 100 (dans ce cas). Cependant, il ne sait pas si :a est inférieur à :b au moment de l'analyse.

Pourquoi exactement, je ne sais pas. Il peut s'agir d'une conception intentionnelle d'Oracle - il est inutile de vérifier si les valeurs définies pour les variables donnent zéro ligne - ou simplement d'un oubli.