Cuando usa variables de vinculación, Oracle se ve obligado a usar poda de partición dinámica en lugar de eliminación de partición estática . El resultado de esto es que Oracle no sabe en el momento del análisis a qué particiones se accederá, ya que esto cambia según las variables de entrada.
Esto significa que cuando usamos valores literales (en lugar de variables de vinculación), sabemos a qué particiones accederá su índice local. Por lo tanto, la count stopkey
se puede aplicar a la salida del índice antes de que eliminemos las particiones.
Al usar variables de vinculación, el partition range iterator
tiene que averiguar a qué particiones está accediendo. Luego tiene una verificación para asegurarse de que la primera de sus variables en las operaciones intermedias tenga un valor más bajo que la segunda (el filter
operación en el segundo plan).
Esto se puede reproducir fácilmente, como muestra el siguiente caso de prueba:
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 |
---------------------------------------------------------------------
Como en su ejemplo, la segunda consulta solo puede filtrar las particiones a una key
en tiempo de análisis, en lugar de las particiones exactas como en el primer ejemplo.
Este es uno de esos raros casos en los que los valores literales pueden proporcionar un mejor rendimiento que las variables de vinculación. Debe investigar si esta es una posibilidad para usted.
Finalmente, dices que quieres 20 filas de cada partición. Su consulta tal como está no hará esto, solo le devolverá las primeras 20 filas de acuerdo con su pedido. Para 20 filas/partición, debe hacer algo como esto:
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
ACTUALIZAR
La razón por la que no está obteniendo la count stopkey
tiene que ver con el filter
operación en la línea 4 del plan "malo". Puede ver esto más claramente si repite el ejemplo anterior, pero sin particiones.
Esto te da los siguientes planes:
----------------------------------------
| 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'))
Como puede ver, hay un filter
extra operación cuando utiliza variables de vinculación que aparecen antes del sort order by stopkey
. Esto sucede después de acceder al archivo index. Esto es verificar que los valores de las variables permitirán que se devuelvan los datos (la primera variable en su medio en realidad tiene un valor más bajo que la segunda). Esto no es necesario cuando se usan literales porque el optimizador ya sabe que 50 es menor que 100 (en este caso). Sin embargo, no sabe si :a es menor que :b en el momento del análisis.
¿Por qué exactamente esto es? No lo sé. Podría ser un diseño intencional de Oracle:no tiene sentido hacer la verificación de la tecla de detención si los valores establecidos para las variables dan como resultado cero filas, o simplemente un descuido.