Introducción
A veces por hábito y por motivos de trabajo se utilizan sentencias SQL simples, sobre todo al desarrollar el típico software de gestión que es tan habitual en todo tipo de empresas. Sin embargo, esta zona de confort no es buena porque hace que se olviden conceptos más complejos al no ponerlos en práctica y porque no incita a reciclarse y profundizar en ciertos aspectos, como las funciones del lenguaje PL/SQL.
Por este motivo a veces se requiere hacer algo aparentemente sencillo pero que se tiende a complicar más de lo necesario al desconocer las herramientas de trabajo, como la función NVL de PL/SQL.
La función NVL
La función NVL forma parte del lenguaje PL/SQL de Oracle y su sintaxis es:
NVL( Expresión1, Expresión2 )
Donde Expresión1 se evalúa a NULL (Not Aplicable o NA según la documentación oficial de Oracle) o a la cadena vacía y en caso de que sea verdadera la evaluación se retorna el valor de la Expresión2. Si el resultado de la evaluación es falso, se retorna el valor de la Expresión1. Por ejemplo:
SHOW NVL( NULL, 'Es nulo' ) Es nulo SHOW NVL( '', 'Es cadena vacía' ) Es cadena vacía SHOW NVL( 5, 'No se muestra' ) 5
También puede ser útil el utilizar la función NVL2 disponible desde Oracle 8i, la cual utiliza tres parámetros devolviendo el resultado de evaluar el tercero de ellos si la Expresión1 es distinto a NULL y a la cadena vacía.
Ejemplos prácticos
A continuación se muestran dos ejemplos que pueden ser de tremenda utilidad en múltiples ocasiones. Son ejemplos sencillos que básicamente nos demuestran que muchas veces se puede mejorar lo existente sin un elevado coste.
Capa de datos uniformada para diferentes aplicaciones
En muchos escenarios, es habitual que una misma base de datos se utilice como capa de persistencia por diversas aplicaciones. Es en ese caso cuando las validaciones de datos pueden transladarse a la capa de persistencia unificando varias de las las validaciones para mantener los datos uniformes y consistentes.
La función NVL puede ser de tremenda utilidad a la hora de devolver resultados y que lleguen con mayor coherencia a las capas superiores. Imaginemos la BD de un hotel. Puede darse el caso de que en la tabla RESERVAS exista un campo COMENTARIOS para añadir información adicional relativa a los clientes. Como pocos clientes son altamente exigentes, habitualmente es un campo que no se informa desde la interfaz de usuario de la aplicación de reservas. En este caso si se realiza la consulta siguiente el resultado sería nulo o la cadena vacía:
SELECT COMENTARIOS FROM RESERVAS WHERE IDCLIENTE = '000000'
El servicio de limpieza por las mañanas hace uso de una nueva aplicación Android que muestra para cada habitación los comentarios del cliente que se va a alojar o está ya alojado en ese momento para prepararle la habitación a su gusto. La aplicación es muy bonita, pero a la hora de proceder con la limpieza de la habitación del cliente 000000 el servicio se encuentra con que la aplicación no muestra nada y como es nueva, pueden pensar que se trata de un error.
Por contra, si se hubiese utilizado la NVL para devolver a las aplicaciones un valor más coherente en caso de que el campo fuese nulo la percepción por los usuarios sería ligeramente diferente:
SELECT NVL( COMENTARIOS, 'N/A' ) FROM RESERVAS WHERE IDCLIENTE = '000000'
Ahora el servicio de limpieza encuentra en el campo de la aplicación un No Aplica, por lo que les da una mayor confianza la aplicación y descartan un posible error.
Valores por defecto en sistemas heredados
Siguiendo con el ejemplo anterior de hotel, una nueva empresa desarrolladora es contratada para ampliar el sistema de reservas para añadir la posibilidad de alquilar plaza de garaje durante la estancia. Hasta la fecha el sistema de reservas del hotel sólo contaba con esta posibilidad de contratar una plaza de garaje por habitación a través del sistema de reservas on-line, por lo que muchos clientes luego cambiaban de opinión una vez alojados y no se les podía ofrecer una.
Una vez desarrollada la aplicación, se reutiliza el campo utilizado por la aplicación de reservas por internet, el PLAZA_GARAJE. La consulta para conocer el número de plazas libres a la hora de modificar la reserva el recpcionista del hotel (se presupone que las 50 habitaciones del hotel están todas ocupadas y que se dispone de una plaza por habitación) :
SELECT COUNT( * ) FROM RESERVAS WHERE fn_esta_activa( IDRESERVA, FECHA_DE_HOY ) AND PLAZA_GARAJE = 'N'
fn_esta_activa es una función PL/SQL que para una reserva comprueba a través de su identificador si está activa en la fecha pasada como segundo parámetro. Tras ejecutar la consulta se devolvieron 0 registros, y como plazas de garaje hay 50 la aplicación cree que no hay plazas libres y es imposbile reservar una. El problema es que de esas supuestas 50 plazas hay 2 reservas hechas on-line a través de una conocida web de reservas (no la web del hotel) que como no trabaja con las plazas del garaje en el campo PLAZA_GARAJE al hacer el insert no se inicializa el campo con valor el valor ‘N’ dejándolo a NULL.
Si la consulta hubiera sido la siguiente, se encontrarían las 2 plazas libres correspondientes a las dos habitaciones reservadas desde la web de reservas externas:
SELECT COUNT( * ) FROM RESERVAS WHERE fn_esta_activa( IDRESERVA, FECHA_DE_HOY ) AND NVL( PLAZA_GARAJE, 'N' ) = 'N'
La función NVL el where de la sentencia hace que para las tuplas con valor desconocido en el campo PLAZA_GARAJE se devuelva el valor N, por lo que se seleccionan y se cuentan las reservas correctamente y será posible que el recepcionista le proporcione una plaza al cliente.
Funciones equivalentes en otros SGBDR
IFNULL de MySQL y MariaDB es una función similar. Y digo similar porque la principal diferencia con la NVL de Oracle reside en que en MySQL un NULL y una cadena vacía son dos cosas diferentes por lo que es mejor consultar la documentación oficial de ambos SGBDRs para estar seguros de que las sentencias se comportan tal y como se espera de ellas (o realizar unas pruebas exhaustivas).
Respecto a PostgreSQL se dispone de la función COALESCE, la cual es ANSI SQL y devuelve el primer argumento que sea distinto de null y que es posible utilizarla para obtener un comportamiento similiar al IFNULL de MySQL/MariaDB o el NVL de Oracle.