Solamente llamar a vistas y a stored procedures

4 comentarios

Una característica que siempre se ve en el código fuente de las aplicaciones bien escritas es que solamente llaman a vistas y a stored procedures.

Y a fuer de ser sinceros, tales aplicaciones son escasas, muy escasas.

Muchos programadores escriben en el código fuente de sus aplicaciones los comandos INSERT, UPDATE, DELETE, y SELECT a columnas de tablas.

Y eso está mal, muy mal.

¿Por qué?

Bueno, hay varios motivos. El autor de este blog varias veces ha evaluado aplicaciones y códigos fuentes escritos por otras personas y al finalizar la evaluación ha elevado un informe detallando lo bueno y lo malo que encontró. Y en el capítulo “Comunicación con la Base de Datos” estos son algunos problemas:

  1. El código fuente es desprolijo. Si alguien escribe los comandos mencionados más arriba en general lo hace de forma desordenada, calculando valores de variables, o realizando otras tareas no relacionadas, y eso además de no ser eficiente queda “feo”.
  2. No permite el trabajo en equipo. Cuando se trabaja en equipo lo ideal es modularizar al máximo para que cada función o cada rutina sea escrita una sola vez y utilizada por muchos programadores. De esa manera si hay algún error solamente puede estar en un lugar y será fácil encontrarlo y corregirlo. Pero si cada quien escribe los comandos en su propio código fuente entonces los errores pueden estar en muchos lugares y encontrarlos y corregirlos demorará mucho más tiempo. Y cualquier cambio a cualquiera de los códigos fuente puede introducir un nuevo error. Y esto inclusive puede ocurrir aunque se trate de un solo programador pero que tiene por ejemplo el INSERT a una sola tabla en dos o más programas.
  3. No hay un responsable de la Base de Datos. Cuando se trabaja en equipo una persona debe ser la encargada de verificar el correcto diseño y funcionamiento de la Base de Datos. Si cada programador escribe lo que se le ocurre en su propio código fuente, tales verificaciones serán imposibles de realizar y los errores posibles, muchísimos.
  4. No se puede verificar que el comando finalizará sin error hasta el momento de ser ejecutado. Por ejemplo, mirando un INSERT en el código fuente no se puede tener la seguridad de que esa fila será insertada o que ocurrirá una excepción de “table unknown”, “column unknown” o alguna otra.
  5. No se puede comprobar la eficiencia del comando. Este es el punto más importante. Los programas de administración gráfica, como el EMS SQL Manager, nos muestran la cantidad de filas leídas, la cantidad de filas extraídas, y en forma gráfica los índices usados. También podemos ver el PLAN utilizado. Así es fácil descubrir que no se está usando un índice cuando sí debería usarse, o que las filas leídas son demasiadas, o que se está usando el índice incorrecto, etc. Y es muy fácil y muy rápido cambiar la vista o el stored procedure con la intención de hacerlo más eficiente. Sin embargo, si escribimos los comandos dentro del código fuente de una aplicación es imposible saber si es eficiente o no lo es. Podemos “creer” que es eficiente y que usa los índices correctos, cuando en realidad no es así. Y hay además otro problema: aunque hoy una vista o un stored procedure sean muy eficientes, podrían dejar de serlo dentro de unos meses cuando las tablas tengan más filas o se hayan cambiado o borrado algunas filas. Escribiendo los comandos INSERT, UPDATE, DELETE, y SELECT a columnas de tablas dentro del código fuente de nuestras aplicaciones jamás podremos estar seguros de que son los más eficientes que podemos tener, en cambio si dichos comandos están dentro de una vista o dentro de un stored procedure sí que podremos tener esa seguridad.

VISTAS-STORED-1

Captura 1. Si haces clic en la imagen la verás más grande

VISTAS-STORED-2

Captura 2. Si haces clic en la imagen la verás más grande

Mirando la Captura 1. sabemos que la vista involucra a 3 tablas y que las 3 tablas usan índices (e inclusive sabemos los nombres de esos índices), y que la cantidad de filas parece estar correcta, sin embargo … ese SORT en la Captura 2. nos llama poderosamente la atención porque los SORT son muy lentos, ya que deben ordenar las filas de las tablas; ese ordenamiento se realiza en la memoria del Servidor (cuando tal cosa es posible) o en el disco duro (cuando no puede ordenarse en la memoria). En general, debemos evitar a los SORT como a la peste, porque indican que la tabla, la vista, el stored procedure seleccionable, o los resultados deberán ser ordenados, y eso puede demorar muchísimo tiempo en tablas grandes.

Si en lugar de escribir una vista, como en el ejemplo de arriba, hubiéramos escrito un SELECT en el código fuente de nuestro lenguaje de programación, ¡¡¡jamás nos habríamos percatado de ese SORT malévolo!!!. Y claro, cuando las tablas tuvieran muchas filas los usuarios se quejarían de la lentitud, pero mientras tanto les hicimos perder mucho tiempo innecesariamente porque nuestra consulta no estaba optimizada.

Conclusión:

Si quieres programar correctamente y eficientemente y profesionalmente, en el código fuente de tus aplicaciones solamente debes llamar a vistas y a stored procedures. Nunca, jamás, y por ningún motivo, ejecutar un INSERT, un UPDATE, un DELETE, o un SELECT a columnas de una tabla.

Los SELECT solamente a vistas.

Los INSERT, UPDATE, DELETE, y algunos SELECT, solamente dentro de un stored procedure.

Cuando se programan aplicaciones siempre se van encontrando errores y problemas aquí y allá. Para disminuir esa cantidad de errores y de problemas, y para optimizar las consultas y los procesamientos, siempre lo mejor es desde el código fuente de nuestras aplicaciones llamar a vistas y a stored procedures, y a nada más.

Artículos relacionados:

Optimizando las consultas

Optimizando las subconsultas

Optimizando los JOIN

Evitando que el optimizador … optimice

¿Por qué usar stored procedures?

Usando un stored procedure como una función

Escribiendo un stored procedure

Usando un PLAN

Algo más sobre PLAN

Entendiendo el contenido de un PLAN

El índice del blog Firebird21

El foro del blog Firebird21

Valores de las variables en un stored procedure

Deja un comentario

Leyendo un comentario que escribió Jaume en el artículo:

Entendiendo a los Stored Procedures

pensé que alguien más podría tener esa misma confusión y por tal motivo estoy escribiendo este artículo. Es para aclarar el comportamiento de las variables en un stored procedure.

Veamos un ejemplo de un stored procedure muy sencillo, sólo para mostrar donde puede existir confusión:

CREATE PROCEDURE VALOR_ANTERIOR
   RETURNS(
      tcNombre TYPE OF COLUMN CLIENTES.CLI_NOMBRE)
AS
BEGIN

   tcNombre = 'PRUEBA';

   FOR SELECT
      CLI_NOMBRE
   FROM
      CLIENTES
   WHERE
      CLI_IDENTI > 1000000
   INTO
      :tcNombre
   DO
      SUSPEND;

   IF (tcNombre = 'PRUEBA') THEN
      SUSPEND;

END;

Evidentemente este es un stored procedure seleccionable (sabemos eso porque tiene el comando SUSPEND dentro de él). Y la pregunta es: ¿qué valor o valores devolverá este stored procedure cuando lo ejecutemos con el comando SELECT?

SELECT
   *
FROM
   VALOR_ANTERIOR

Bien, eso depende de si hay alguna fila que tenga en la columna CLI_IDENTI un valor mayor que 1000000 ó no. Si hay una o más filas, entonces devolverá el nombre de los respectivos clientes pero si ninguna fila cumple con esa condición entonces devolverá la palabra ‘PRUEBA’.

¿Por qué devuelve ‘PRUEBA’ y no devuelve NULL?

Uno podría pensar que si ninguna fila cumple con la condición entonces debería devolver NULL, sin embargo no es así, devuelve el valor que anteriormente tenía la variable tcNombre, en este caso ‘PRUEBA’. ¿Por qué eso?

Eso es porque un SELECT puede devolver cero filas, eso ocurre cuando la tabla no tiene filas o cuando ninguna fila cumple con la condición. En ese caso el valor que tenían las variables asignadas por el SELECT (las que se encuentran después de la cláusula INTO) no puede cambiar ya que ninguna fila fue retornada. La asignación a esas variables se hace solamente después de obtener una fila, como es lógico.

En consecuencia, si ninguna fila es retornada todas esas variables mantienen el valor que tenían anteriormente, porque ninguna asignación fue hecha a ellas. En este caso tcNombre seguirá valiendo ‘PRUEBA’.

Para que tcNombre valiera NULL, el Firebird tendría que asignarle NULL antes de ejecutar al SELECT pero ¿para qué haría eso? sería una causa de conflicto porque una columna de un SELECT puede legítimamente valer NULL y en ese caso ¿cómo se diferenciaría entre un NULL previamente asignado y un NULL como valor legítimo de una columna? No habría forma de diferenciar a un NULL del otro, por lo tanto el Firebird hace lo más lógico, coherente, y racional: asignarle valores a las variables solamente después de obtener una fila del SELECT, nunca antes.

Así que, el Firebird hace lo correcto.

Por lo tanto, si un SELECT no retorna filas, todas las variables que se encuentren después de la claúsula INTO mantendrán el valor que tenían antes del SELECT.

No asumas que el valor de dichas variables será NULL, porque podría no ser así.

Artículos relacionados:

Entendiendo a los Stored Procedures

El índice del blog Firebird21

El foro del blog Firebird21