Un PARSE por cada ejecución quiere decir que el cursor se abre y se cierra en cada uso. Aunque sea liviano, cada parse cuesta una llamada, CPU y contención en la library cache; el SQL preparado una vez y ejecutado muchas evita todo eso.
Síntomas
En el Diagnóstico aparece el hallazgo Sentencia #n: un PARSE en cada ejecución (n PARSE, n EXEC) (PARSE_POR_EXECUCAO), por ejemplo Sentencia #1: un PARSE en cada ejecución (2.000 PARSE, 2.000 EXEC). El texto dice que el cursor se abre y se cierra en cada uso y termina con Ver sentencia #1, que abre el detalle de la sentencia.
El hallazgo aparece para las sentencias muy ejecutadas en las que casi toda ejecución (EXEC) viene con un PARSE propio, empezando por las que más pesan. La gravedad es baja y pasa a media cuando el PARSE de la sentencia pesa más en el tiempo total; ese peso es el porcentaje junto al título.
En una fila con la etiqueta N variantes, que suma SQL con solo los literales diferentes, el PARSE con hard parse es el de cada valor nuevo, y lo trata el hallazgo de literales en lugar de bind. Este hallazgo solo aparece en esa fila cuando las variantes se repiten y su PARSE es casi todo liviano: el cursor de cada variante se abre y se cierra en cada uso.
Causa
La aplicación prepara el SQL, lo ejecuta una vez y cierra el cursor, y repite el ciclo en la próxima ejecución. Cada ciclo cuesta una llamada PARSE más y puede costar una ida y vuelta más en la red. Aunque el parse sea liviano (soft parse, sin mis=1), gasta CPU y disputa la library cache con las otras sesiones.
Es común en código que crea el statement dentro del bucle, en drivers sin caché de statements y en capas de acceso que abren y cierran el cursor en cada llamada.
Solución
- En la aplicación, prepare el SQL una vez y ejecútelo muchas: guarde el statement preparado y reutilícelo, en lugar de crearlo y cerrarlo en cada ejecución.
- Active la caché de statements del driver, que mantiene los cursores abiertos entre usos. En el JDBC de Oracle, por ejemplo:
OracleConnection oconn = conn.unwrap(OracleConnection.class); oconn.setImplicitCachingEnabled(true); oconn.setStatementCacheSize(50); - Revise
session_cached_cursors. En PL/SQL, define cuántos cursores mantiene abiertos PL/SQL, y el PARSE repetido desaparece. En la aplicación cliente, abarata cada PARSE, pero la llamada sigue en el trace: lo que la quita son los pasos 1 y 2.select value from v$parameter where name = 'session_cached_cursors'; - Si la sentencia es SQL dinámico armado con los valores en el texto, el problema es otro: vea literales en lugar de bind.
Cómo confirmar
Genere un trace nuevo de la misma operación después del cambio y abra el detalle de la sentencia. En la tabla Llamadas, la Cantidad de PARSE debe quedar muy por debajo de la de EXEC, y el hallazgo desaparece.
En el perfil, CPU: PARSE dbcalls debe bajar. Con la caché de statements en la aplicación, las Ocurrencias de SQL*Net message from client también pueden bajar.
En el informe
En el informe de ejemplo, la sentencia #1 tiene 1 PARSE y 1 EXEC: un PARSE por ejecución, pero en una sola ejecución, sin hallazgo. El informe del caso 1, de un trace real, tiene este hallazgo (Diagnóstico del caso 1). Los enlaces de abajo abren las secciones del ejemplo:
- Diagnóstico: el hallazgo y el enlace Ver sentencia #n.
- Detalle de la sentencia #1: en la tabla Llamadas, la Cantidad de PARSE y de EXEC lado a lado.
- Tipos de llamada: PARSE y EXEC de toda la sesión, con Misses.
- Perfil, fila CPU: PARSE dbcalls: la CPU gastada en parse.
Esperas como library cache: mutex X y cursor: pin S, de la clase Concurrency, también acompañan el PARSE en exceso: vea contención por estructuras internas. El detalle de la sentencia tiene página propia: detalle de la sentencia.