Ir al contenido principal

ISNULL y MAX, ¡cuidado! El orden importa

Imaginemos que queremos obtener el valor máximo de una columna de una tabla que puede contener valores nulos y que, en caso de no existir ningún valor en ella o ser todos los existentes nulos, deseamos obtener un cero.

T-SQL nos ofrece los operadores MAX e ISNULL para conseguir tal propósito, pero debemos tener cuidado con el orden en que los usamos, pues si la consulta SELECT que estamos ejecutando no devuelve ningún registro, el resultado podría no ser el que esperamos.

MAX, como fácilmente se puede deducir de su nombre, nos devolverá el máximo valor de la expresión que indiquemos entre paréntesis. En nuestro caso, un nombre de columna. Se ignorarán los valores NULL.

Como, en caso de no existir ningún valor en la columna, queremos que nos devuelva un cero, usaremos la función ISNULL.

ISNULL sustituye los nulos del campo indicado por el valor indicado tras la coma.

Enunciado

Así, supongamos que tenemos una tabla de contactos, y queremos extraer de ella la máxima edad de nuestros contactos de Italia. Age es la columna que contiene la edad, admite valores nulos, ya que no podemos disponer de ese dato para  todos nuestros contactos.

Como queremos eliminar los valores nulos, usaremos ISNULL para reemplazarlos por un cero. Una vez hayamos hecho esto, obtendremos el máximo valor con MAX. Fácil, ¿no? La consulta quedaría como sigue:

SELECT MAX(ISNULL(Age, 0))
FROM Contact
WHERE Country = N'Italy'

La consulta funcionará correctamente... ¡siempre que tengamos algún contacto cuyo país sea Italia!

Sí, por ejemplo, tenemos la siguiente distribución de datos:


ContactId   Country    Age
----------- ---------- ------
1               Italy          57
2               Italy          NULL

Obtendremos un 57, tras haber convertido el NULL en un cero.

Si solamente existiese el segundo registro con país igual a Italia, obtendríamos un cero, procedente del NULL.

Pero, ¿qué pasaría si no tuviésemos ningún registro cuyo país es Italia? Para nuestra sorpresa, el resultado de la consulta sería ¡NULL!


Explicación del error

MAX siempre devuelve un valor del tipo de datos de la expresión que queremos evaluar. Si la expresión no contiene nada que evaluar, MAX nos devuelve un bonito nulo. Así de simple.

El ISNULL que estamos aplicando a la expresión -en nuestro caso, la columna Age de la tabla Contact- no puede hacer nada, ya que no existen NULL en esa columna que transformar en cero. Es una método de transformación, de reemplazo. Si la consulta original no devuelve registros, ISNULL no hará que aparezcan.

Por ejemplo, podéis comprobarlo fácilmente con la siguiente consulta:

SELECT ISNULL(Age, 0)
FROM Contact
WHERE 1 = 0

Veréis como SQL Server os devuelve: (0 row(s) affected)

Solución

Evitar la aparición de estos inesperados valores NULL por sorpresa es sencillo. Sólo tenemos que invertir el orden en el que aplicamos ISNULL y MAX.

Sabemos que MAX siempre nos va a generar un resultado que puede ser desde el valor que buscamos hasta un NULL en caso de que no existan registros o que todos los registros de la expresión sean, a su vez, nulos. Así pues, será la primera operación que apliquemos.

Una vez hayamos obtenido un único valor con MAX, para asegurarnos de que no devolvemos un valor nulo, es el turno de usar ISNULL, y decimos que, en ese caso, queremos que la consulta nos devuelva un cero.

Esta sería, pues, la consulta correcta:

SELECT ISNULL(MAX(Age), 0)
FROM Contact
WHERE Country = N'Italy'

Es sencillo, pero también es fácil equivocarse y escribirla en el orden incorrecto, lo que puede hacer a nuestra consulta devolver un inesperado e incómodo NULL en el momento menos oportuno.

Comentarios

  1. Estaba buscando algo sobre Transact y me aclaró el concepto, Gracias

    ResponderEliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  3. hola quisiera saber quien me puede ayudar lo que pasa es que tengo una tabla de precios y tengo que sacar el MAX pero la misma tabla pero tambien ocupo hacer un select * al mismo campo , quien me ayudaria , Saludos

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Aprendiendo a usar LEFT OUTER JOIN

En esta entrada pretendemos explicar los diferentes resultados obtenidos por distintas construcciones de consultas que, aparentemente, deberían producir el mismo conjunto de resultados. Así, veremos las diferencias entre filtrar los resultados de una query en la unión (Join) mediante condiciones ON y mediante cláusulas WHERE.

Variantes del SELECT COUNT con DISTINCT

Seguramente, muchos de vosotros habréis usado en innumerables ocasiones la función de T-SQL COUNT , que no hace sino devolver un número de registros: de una tabla, de un conjunto de resultados, etc... En una de sus aplicaciones, combinado con el DISTINCT -uno de los dos argumentos que admite- COUNT nos devuelve el número de valores únicos no nulos de la tabla o conjunto de resultados que estemos consultando. Pero ¡ojo! Cuidado con la sintaxis , o podemos obtener el valor equivocado sin darnos cuenta. No es lo mismo: SELECT COUNT (DISTINCT NombreCampo) FROM NombreTabla que: SELECT COUNT(*), DISTINCT NombreCampo FROM NombreTabla

Script para obtener el tamaño de todas las tablas de la base de datos

En algunas ocasiones podemos vernos con la necesidad de conocer qué tablas de nuestra base de datos están ocupando más espacio en disco. Por ejemplo, si disponemos de SQL Server Express , cuyas bases de datos están limitadas a 4GB o 10GB, según la versión que estemos usando -4, hasta 2005; 10, a partir de 2008-, aparte de usar las opciones de comprimir la base de datos, poner el log en el modo simple de recuperación o ajustar las políticas de crecimiento automático de nuestros ficheros, podemos necesitar averiguar qué tablas crecen más para tomar las decisiones oportunas.