Pegar un literal en el SQL multiplica por mil el parseo en Oracle
Un laboratorio sobre Oracle Database Free mide lo que cuesta construir SQL concatenando valores: mil cursores y 38 MB de shared pool frente a un cursor y 39 KB con una variable de bind.
Pegar el valor dentro de la cadena de SQL en lugar de usar una variable de bind convierte cada consulta en una sentencia nueva para Oracle. El motor la analiza desde cero, le reserva memoria en el shared pool y la guarda como si nunca hubiera visto nada igual, porque para él no lo es: WHERE id = 4711 y WHERE id = 4712 son dos textos distintos y, por tanto, dos cursores. Un laboratorio publicado en GitHub mide el destrozo sobre Oracle Database Free: 1.000 ejecuciones de la misma consulta con literales dejan alrededor de mil cursores, mil hard parses y unos 38 MB de shared pool. La misma consulta con :b deja un cursor, un hard parse y unos 39 KB.
Qué cuesta un parse
Antes de devolver una fila, Oracle comprueba la sintaxis, resuelve tablas y columnas, valida privilegios y hace que el optimizador decida un plan. Todo ese trabajo se cachea: si llega exactamente el mismo texto, reutiliza el cursor y se ahorra el proceso. El hard parse es el camino caro —construir el cursor, reservar memoria, tomar el mutex de la library cache— y el soft parse el barato, pero ninguno es gratis. Lo que dispara la reutilización es el texto, comparado casi byte a byte.
Con un bind, el texto es idéntico pase lo que pase y el cursor se comparte: mil ejecuciones, un parse. El banco de pruebas hace las dos vueltas con SELECT COUNT(*) FROM widgets WHERE id = <n>, limpia el shared pool antes de cada una y lee los contadores de V$SQL y V$MYSTAT. Las 1.001 ejecuciones que registra el cursor con bind son la prueba de que todas fueron a parar al mismo sitio.
Cuándo el literal sí es lo correcto
El coste no es solo CPU malgastada. Insertar un cursor nuevo exige un mutex sobre la library cache, así que miles de sesiones analizando a la vez se ponen en cola; de ahí los cursor: pin S wait on X y los library cache: mutex X que coronan un AWR con perfil de parseo. Los cursores de un solo uso desalojan a los reutilizables y fragmentan el pool hasta llegar al ORA-04031. Y con cada sentencia distinta, el apartado de SQL ordenado por ejecuciones deja de significar nada: la consulta caliente queda enterrada entre miles de gemelas.
Dicho esto, "siempre bind" es un mito. Un bind esconde el valor en el momento del parse, así que el optimizador planifica para un valor genérico, con la ayuda del bind peeking y el adaptive cursor sharing. En analítica y reporting, con pocas ejecuciones y datos sesgados con histogramas, interesa que vea el valor real y elija el plan. En OLTP, el bind es lo correcto por defecto.
Queda por ver cuánto tarda esto en reaparecer en la próxima revisión de código. El banco de pruebas se ejecuta en cada push de integración continua y el fallo es reproducible en una base gratuita, así que no hay excusa para diagnosticarlo a ojo.

