====== Buenas prácticas de programación con SQL y PL/SQL ======
Extraído de una presentación de Steven Feurestein https://community.oracle.com/servlet/JiveServlet/download/915011-2-163991/Make%20No%20Small%20Plans%20PDF.pdf
* Reutilización
* Usa bind variables (con JDBC usar parámetros https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html#create_ps).
* Usa código almacenado en la BD (paquetes, procedimientos, funciones, etc).
* Usa vistas materializadas en lugar de vistas con DBlinks
* Acceso a Datos
* Calcula estadísticas de tablas, columnas e índices
* El acceso más rápido a un datos es obteniendo directamente la fila (ROWID).
* Revisa el plan de ejecución (explain plan)
* Seguridad
* Usa bind variables.
* El usuario que ejecuta la aplicación NO puede ser el propietario de las tablas.
* Construye una API con PL/SQL para consultar y actualizar los datos, de forma que el usuario que ejecuta la aplicación NO tenga acceso directo a las tablas.
* Con PL/SQL solo se comparten en SGA las constantes estáticas de paquete (p.e. valor fijo de nº o string, pero NO un valor de secuencia).
* Minimizar los cambios de contexto entre motor PL/SQL y SQL, que ocurren cada vez que se ejecuta una sentencia SQL dentro de PL/SQL.
* No repetir sentencias (usar funciones/procedures)
* Usar BULK COLLECT y FORALL
* Una función en una sentencia SQL, a su vez, NO puede contener ninguna sentencia SQL (debe ser determinística).
* Hacer GTs para estandarizar la escritura de SQL:
* Estar al día sobre las novedades en SQL
* ¿cómo, cuándo y dónde escribir SQL?
* Novedades SQL
* Funciones analíticas, como LAG y LEAD q permiten trabajar con las filas anterior y siguiente para calcular diferencias.
* WITH, para definir "vistas" reutilizables en una sentencia compleja.
* Flasback query.
* ANSI JOIN: cambiar "(+)" por sintaxis completa.
* Usar subconsultas como si fuesen funciones.
* Novedades 12.1
* Patrones (patter matching) y limit (row limiting)
* Columnas en memoria (in-memory column store)
* JSON nativo
* Simplificar SQL (fuera sentencias complejas)
* NUNCA repitas una sentencia SQL.
* Oculta las sentencias SQL en una capa de acceso a datos.
* SQL como servicio = vistas + paquetes
* Gestión de excepciones
* NO consultes tablas directamente, hazlo mediante vistas.
* Separa datos y código en esquemas diferentes.
* Rutas absolutas para todas las columnas y variables q referencias en una consulta.
* Gestión de excepciones (ejemplo Sistema de Error Logger https://github.com/OraOpenSource/Logger)
* Pasarle al Sistema de Log las variables locales y el estado de la app.
* Usar transacciones autónomas para el Sistema de Log.
* Usar EXCEPTION_INIT para asignarle nombre a los errores, NO usar directamente nºs y/o mensajes de error.
* Trazas (solo en el entorno de producción, por defecto desactivada).
* Usar Oracle Trace, NUNCA dbms_output.put_line()
* Usar DBMS_APPLICATION_INFO
* Tb puedes usar Logger (visto antes) https://github.com/OraOpenSource/Logger
* Testing
* SQLdeveloper Unit Testing
* [[plsql:utplsql|Test unitarios del código PL/SQL con utPLSQL]]
* Escribe Código Mantenible
* Regression test
* NO repetir nada (no solo constantes, tampoco varchar2 y lógica de negocio).
* Usar %TYPE y %ROWTYPE (en lugar de varchar2)
* Usar funciones para encapsular fórmulas y lógica de negocio
* Minimizar la parte ejecutable de los bloques, NO más de 50 líneas.
* Ocultar expresiones complejas en constantes o funciones.
* Optimizar el código
* Cachear (function result cache, q solo está en versión enterprise de Oracle)
* IMPORTANTE: la optimización de BD (DBA) no llega a más del 20% del efecto, el 80% se consigue escribiendo código mantenible.
* Identificar cuellos de botella haciendo tuning más granular, usando DBMS_PROFILER y DBMS_HPROF.
* Usar variables de paquete (colecciones) para cachear tablas pequeñas de códigos, estáticas, teniendo en cuenta q SOLO sirve para mantener los datos durante la MISMA SESION (mejor en procesos batch, OJO con apps web q usarn datasource donde la sesión web NO ES IGUAL a la sesión de BD). Consumen PGA.
* Function RESULT CACHE.
* No se cachean tipos complejos como registros (record) con CLOBS, colecciones, etc.
* Solo se cachean los parámetros de entrada y el valor de salida
* Si usar INVOKER RIGHTS necesitas v12.1 para q use result cache
* OJO si usas VPD (virtual private database) y/o contextos (SYS_CONTEXT)
* Sobre todo cachear tablas cuyos datos no cambian durante una sesión
* BULK COLLECT. Se usa en SELECTs para cargar todas las filas en una colección, de un golpe.
* Ejemplos sencillos
SELECT * BULK COLLECT INTO collection(s) FROM table;
FETCH cur BULK COLLECT INTO collection(s);
EXECUTE IMMEDIATE query BULK COLLECT INTO collection(s);
* No sirve NO_DATA_FOUND, hay q controlar cuando se vacía la colección.
* Se puede usar LIMIT para limitar el nº de filas a procesar
OPEN emps_in_dept_cur;
LOOP
FETCH emps_in_dept_cur
BULK COLLECT INTO emps LIMIT 1000;
EXIT WHEN emps.COUNT = 0;
process_emps (emps);
END LOOP;
* NO USES Bulk Collect si tu bucle funciona bien, o si NO contiene sentencias DMLs.
* FORALL. Se usa en UPDATE/INSERT/DELETE, para actualizar todas las filas de un golpe, desde una colección.
* Ejemplo
FORALL indx IN low_value .. high_value
UPDATE employee
SET salary = newsal_in
WHERE employee_id = list_of_emps (indx);
* SQL%ROWCOUNT cuento el total de filas modificadas por todo el FORALL
* SQL%BULK_ROWCOUNT cuenta el nº de filas modificadas en cada ciclo del bucle FORALL
* Si tienes bucles q contienen sentencias DML, cambia el proceso por otro en 3 fases:
* Primero, usa BULK COLLECT para cargar los datos en una colección.
* Segundo, haz los cambios en la colección.
* Tercero, usa FORALL para aplicar los cambios en la BD desde la colección.
* Mejoras en 12c de WITH https://oracle-base.com/articles/12c/with-clause-enhancements-12cr1
:!: Este contenido es una copia de https://wiki.um.es/wikis/programador/doku.php?id=plsql:optimizacion
--- //[[juanlu@um.es|JUAN LUIS SERRADILLA AMARILLA]] 15/09/2022//