Pregunta

Tenemos dos tablas de este modo:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

Hay índices definidos por

  • Evento (id) (PK)
  • ProcessedEvent (event_id, proceso)

La primera representa eventos en una aplicación.

El segundo representa el hecho de que un determinado evento procesos consiguió por un determinado proceso. Hay muchos procesos que necesitan para procesar un determinado evento, por lo que hay varias entradas en la segunda tabla para cada número de la primera.

Con el fin de encontrar todos los eventos que necesitan procesamiento se ejecuta la siguiente consulta:

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

Las estadísticas son hasta la fecha

Como la mayoría de los eventos se procesan, creo que el mejor plan de ejecución debe ser algo como esto

  • recorrido de índice completo en el Índice de ProcessedEvent
  • recorrido de índice completo en el índice de eventos
  • contra la unión entre los dos
  • Acceso a la mesa con el resto
  • filtro

En lugar de Oracle hace lo siguiente

  • recorrido de índice completo en el Índice de ProcessedEvent
  • escaneo completo de tabla en la tabla de eventos
  • filtrar la tabla de eventos
  • contra la unión entre los dos conjuntos

Con una sugerencia de índice consigo Oracle para hacer lo siguiente:

  • recorrido de índice completo en el Índice de ProcessedEvent
  • recorrido de índice completo en el índice de eventos
  • Acceso de mesa en la tabla de eventos
  • filtrar la tabla de eventos
  • contra la unión entre los dos conjuntos

que es realmente estúpido en mi humilde opinión.

Así que mi pregunta es: ¿cuál podría ser la razón de Oracle para insistir en el acceso a la tabla temprana


Además: El rendimiento es malo. Estamos arreglando el problema de rendimiento seleccionando sólo los Event.IDs y luego ir a buscar manualmente '' las filas necesarias. Pero, por supuesto, eso es sólo un trabajo alrededor.

¿Fue útil?

Solución

SCAN su índice completo será probablemente más rápido que un escaneo completo de tabla ya que el índice es probable "más delgada" de la tabla. Aún así, el instala ÍNDICE SCAN es una lectura completa del segmento y que será aproximadamente el mismo costo que el escaneo completo de tabla.

Sin embargo, también se va a añadir un ACCESO POR ROWID TABLA paso. Es un paso caro: una lógica IO por línea para el acceso ROWID mientras que usted consigue uno IO lógica por varios bloques (dependiendo de su db_file_multiblock_read_count parameter) para el escaneo completo de tabla.

En conclusión, el optimizador calcula que:

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

Actualizar : El escaneo completo de tabla también permite que el filtro del tipo antes que en la ruta completa ÍNDICE SCAN (ya que el índice no sabe de qué tipo es un evento), por lo tanto, reducir el tamaño de el conjunto que va a ser anti unido (todavía otra ventaja del escaneo completo de tabla).

Otros consejos

El optimizador tiene muchas cosas que no tienen sentido en un primer momento, pero tiene sus razones. Ellos pueden no ser siempre derecho , pero son comprensibles.

La tabla de eventos puede ser más fácil de exploración completa en lugar de por el acceso rowid debido a su tamaño. Podría ser que hay significativamente menos operaciones de IO involucrados para leer toda la tabla secuencial que leer trozos y piezas.

es el rendimiento malo, o simplemente estás preguntando por qué el optimizador hizo eso?

No puedo explicar el comportamiento del optimizador, pero mi experiencia ha sido evitar "no en" a toda costa, reemplazándolo con MENOS, así:

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

He visto órdenes de magnitud en el rendimiento de las consultas con las transformaciones similares.

Algo así como:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

podría funcionar lo suficientemente rápido (al menos mucho más rápido que NOT IN).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top