Actualizado el 22 de Junio del 2017 (Publicado el 14 de Enero del 2017)
1.063 visualizaciones desde el 14 de Enero del 2017
272,9 KB
14 paginas
Creado hace 14a (19/06/2010)
Guía de NULL en Firebird
Comportamiento y dificultades de NULL en Firebird
Paul Vinkenoog
Traducción al castellano de Víctor Zaragoza
22 de julio de 2005 Versión de documento 0.2-es
Introducción
Frecuentemente, preguntas de soporte en las listas de correo de Firebird hablan de
“cosas raras” que pasan con los valores NULL en el SQL de Firebird. El concepto
parece difícil de entender – quizá en parte por el nombre, que sugiere una “nada” que no
puede hacer ningún daño si se lo sumas a un número o lo concatenas al final de un
string. En realidad, realizar esas operaciones devolverán también la expresión NULL.
Este artículo explora el comportamiento de NULL en el SQL de Firebird, apunta fallos
comunes y te enseña como manejar de manera segura expresiones que contengan NULL
o resulten en NULL.
Nota:
Algunas sentencias y ejemplos en esta guía han sido tomados de Firebird Quick Start
Guide, inicialmente publicado por IBPhoenix, ahora parte de Firebird Project.
Qué es NULL?
En SQL, NULL no es un valor. Es un estado que indica que el valor de ese item es
desconocido o no existente. No es cero o blanco o una “cadena vacía” y no se comporta
como ninguno de esos valores. Pocas cosas en SQL llevan a tanta confusión como
NULL, y será difícil de entender mientras no entiendas la siguiente simple definición:
NULL significa desconocido.
Déjame repetirlo:
NULL significa desconocido.
Retén esta línea en tu mente mientras leas el resto de este artículo y verás como muchos
de los resultados que parecen absolutamente ilógicos que obtengas con NULL,
prácticamente se autoexplicarán.
NULL en expresiones
Como muchos de nosotros hemos encontrado, para nuestro disgusto, NULL es
contagioso: úsalo en una expresión numérica, texto o fecha/hora y el resultado siempre
es NULL. Úsalo en una expresión lógica y el resultado depende del tipo de operación y
el resto de valores implicados.
Por cierto, nota que en versiones anteriores a Firebird 2.0 es normalmente ilegal usar la
constante NULL directamente en operaciones o comparaciones. Cuando veas NULL en
las expresiones siguientes, léelas como “un campo, variable u otra expresión que
resuelve en NULL”.
Expresiones que devuelven NULL
Las expresiones en esta lista siempre devuelven NULL.
• 1 + 2 +3 + NULL
• ‘Hogar ‘ || ‘dulce ‘ || NULL
• MiCampo = NULL
• MiCampo <> NULL
• NULL = NULL
• not (NULL)
Si tienes dificultades en entender por qué, recuerda que NULL significa “desconocido”.
Además mira en la siguiente tabla donde hay explicaciones por caso. En la tabla, no
hemos escrito NULL en las expresiones (como ya hemos dicho, es, a menudo, ilegal); en
vez de ello, hemos usado dos entidades A y B que son ambas NULL. A y B pueden ser
campos, variables o subexpresiones enteras en su derecho – como son NULL, se
comportan de la misma manera que expresiones cerradas.
Porque:
Tabla 1. Operaciones sobre entidades NULL A y B
Si A y B son NULL, entonces:
1 + 2 + 3 + A
‘Hogar ‘ || ‘dulce ‘ || NULL
MiCampo = A
MiCampo <> A
A = B
Not (A)
Es:
NULL
Si A es desconocido, entonces 6+A también es
desconocido
NULL
Si A es desconocido, entonces ‘Dulce hogar‘ || A
es también desconocido
NULL
Si A es desconocido, no puedes decir que
MiCampo tenga el mismo valor…
NULL
...ni puedes decir que MiCampo tenga distinto
valor
NULL Con A y B desconocidos, es imposible saber si
NULL
son iguales
Si A es desconocido, su inverso también
NULL en expresiones booleanas
Hemos visto que not(NULL) devuelve NULL. Para los operadores and y or, las
cosas son un poco más complicadas:
• NULL or false = NULL
• NULL or true = true
• NULL or NULL = NULL
• NULL and false = false
• NULL and true = NULL
• NULL and NULL = NULL
El SQL de Firebird, no tiene un dato de tipo booleano (lógico); no hay unas constantes
true o false existentes. En la columna de la izquierda de la siguiente tabla (true) and
(false) representan expresiones que devuelven true/false;
Porque:
Tabla 2. Operaciones lógicas (booleanas) sobre una entidad NULL A
Si A es NULL, entonces:
A or false
A or true
A or A
A and false
A and true
A and A
Es:
NULL
True
NULL
False “A y false” es siempre false. El valor de A no
NULL
NULL
“A or false” siempre tiene el valor de A, que es
desconocido
“A or true” siempre es true. El valor de A no
importa
“A or A” siempre equivale a A, que es NULL
importa
“A y true” siempre tiene el valor de A el cuál es
desconocido
“A and A” siempre equivale a A, que es NULL
Todas estas expresiones están en concordancia con la lógica booleana. El hecho de que,
para calcular “X or true” y “X and false”, simplemente no necesites saber el
valor de X, es también la base de una característica que conocemos en varios lenguajes
de programación: evaluación de circuitos cortos booleanos.
Más lógica (o no)
Los resultados de circuitos cortos obtenidos arriba, pueden llevarte a las siguientes
ideas:
•
0 veces X equivale a 0 para cada X. Por tanto, igual que el valor de X es
desconocido, 0 * X es 0. (Nota: esto sólo sucede si el tipo de dato de X sólo
contiene números, no NaN o infinitos).
• La cadena vacía está ordenada lexicográficamente antes de cada otra cadena. Por
tanto S >= ‘’ es verdad siempre independientemente del valor de S.
• Cada valor equivale a sí mismo, independientemente de si es desconocido o no.
Por tanto, aunque A = B justificadamente se devuelve NULL si A y B son
entidades NULL diferentes, A = A siempre devuelve true, igual que A es NULL.
¿Cómo está esto implementado en el SQL de Firebird? Bueno, siento informarte que, a
pesar de esta convincente lógica – y la analogía con los resultados explicados arriba –
las siguientes expresiones se resuelven todas con NULL:
• 0 * NULL
• NULL >= ‘’
• ‘’ <= NULL
• A = A (con A como un campo o variable NULL)
Demasiado para la coherencia.
NULL en funciones agregadas
En funciones agregadas como COUNT, SUM, AVG, MAX, y MIN, NULL se maneja de
manera diferente: para calcular el resultado, sólo se tienen en consideración los campos
con valores no-NULL. Esto es, si tienes esta tabla:
Sueldo
37
<NULL>
5
12
<NULL>
Nombre
Juan
Perico
Andrés
Roberto
Antonio
MiTabla
ID
1
2
3
4
5
… la sentencia select sum(Sueldo) from MiTabla devuelve 54, que es
37+5+12. Si sumáramos todos los valores, el resultado debería haber sido NULL. Para
AVG, los campos no-NULL son sumados y la suma dividida entre el número de campos
no-NULL.
Hay una excepción en esta regla: COUNT(*) devuelve el número de todas las filas,
incluidas todas aquellas con campos en NULL. Pero COUNT(NombreDeCampo) se
comporta como las otras funciones agregadas y cuenta aquellas filas que tienen campos
con contenido no-NULL.
Otra cosa a tener en cuenta: COUNT(*) y COUNT(NombreDeCampo) jamás
devuelven NULL: si no hay filas en el grupo, ambas funciones devuelven 0. Además,
COUNT(NombreDeCampo) devuelve 0 si todos los NombreDeCampo del grupo son
NULL. Las otras funciones agregadas devuelven NULL en tales casos. Ten en cuenta que
SUM devuelve NULL si se usa en un grupo de registros vacío, lo que es contrario a la
lógica común.
Manejo de NULL en UDF’s
Las UDF’s (User Defined Functions o Funciones Definidas por el Usuario) son
funciones no internas en el motor, por tanto definidas en módulos separados. Firebird
viene con dos librerías UDF: ib_udf (heredada de Interbase) y fbudf. Puedes agregar
más librerías, por ejemplo comprándolas o descargándotelas o bien escribiéndolas tú
mismo. Las UDF no pueden ser usadas “fuera de la caja”, deben ser “declaradas” en la
base de datos primero. Esto es cierto incluso con las UDF que vienen con Firebird.
Conversiones NULL <-> no-NULL no deseadas
Enseñarte como declarar, escribir y usar UDF’s está fuera del ámbito de esta guía. No
obstante, debemos avisarte de que las UDF’s pueden efectuar, ocasionalmente,
conversiones NULL no esperadas. Esto puede, en ocasiones, resultar de introducir un
valor NULL y su conversión a un valor normal y en otra ocasiones en la “nulificación”
de un valor válido de entrada ‘’ (una cadena vacía).
La principal causa de este problema es la llamada “a la antigua usanza” de la UDF. No
es posible pasar un valor NULL como entrada a la función. Cuando una UDF como
LTRIM (recortar por la izquierda) se llama con un argumento NULL, el argumento es
pasado a la función como una cadena vacía. Desde dentro de la función no hay manera
de decir si el argumento pasado es una cadena vacía real o un valor NULL. ¿Entonces
qué hace el implementador de la función? Tiene que tomar una decisión: o bien tomar el
argumento como entró o asumir que es un NULL y tratarlo acorde a ello.
Dependiendo del tipo de dato del resultado, devolver NULL es posible igual que
recibirlo como argumento no lo es. De este modo, estas situaciones inesperadas pueden
suceder:
• Puedes llamar a una UDF con un argumento NULL, ésta lo recibe como un 0 o
como una cadena vacía ‘’. Por medio de la función, este valor no vuelve a ser
NULL: el resultado es un valor no-NULL.
• Puedes llamar a una UDF con un argumento válido como 0 o ‘’ (cadena vacía).
Se pasa el argumento “como es” (obviamente), pero la función considera que el
argumento realmente representa un NULL, lo trata como un agujero negro y
devuelve un NULL.
Ambas conversiones no son deseadas, pero la segunda probablemente más que la
primera (mejor validar algo que es NULL que “nulificar” un valor válido). Volviendo a
nuestro ejemplo con LTRIM: hasta e incluyendo Firebird 1.0.3, esta función devolvía
NULL si se le pasaba como argumento una cadena vacía. Desde la versión 1.5, nunca
devuelve NULL. En estas recientes versiones, las cadenas NULL son “TRIMeadas” a
cadenas vacías – lo cual es incorrecto, pero considerado el menos malo de los dos casos;
en el caso anterior, c
Comentarios de: Guía de NULL en Firebird (0)
No hay comentarios