1 de agosto de 2014

Manipular valores NULL en VFP

Artículo original: Handling NULL Values in VFP
http://weblogs.foxite.com/andykramek/archive/2005/12/04/1016.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York

El otro día estuve recordando algunos de los problemas que podían surgir en mi código cuando se encontraba un valor NULL inesperado. Esto me hizo retomar temas de comprensión sobre la manipulación de NULL en VFP y sobre esto tratará este blog. Vamos a comenzar considerando los posibles estados en los que puede existir un valor de VFP ( ya sea un campo de una tabla, o una variable). En Visual FoxPro hay en realidad tres condiciones:
  • el valor puede ser igual a algo (.T.)
  • el valor puede no ser igual a algo (.F.)
  • el valor puede estar en un estado en el que su valor no puede ser determinado (NULL)
Los primeros dos casos no tienen problema y podemos controlar estos casos diariamente en nuestro código. Es el tercero el que nos puede causar problemas, sin que lo controlemos. Frecuentemente encuentro útil, pensar NULL pensando que NULL significa "Yo no se". Esto ayuda a tener sentido el comportamiento de los valores nulos cuando son encontrados.

Lo primero a recordar sobre los nulos es que la respuesta a cualquier comparación que involucre un valor NULL es siempre NULL. Consideremos el siguiente código:
luVal = NULL
? luVal > 0
? luVal = "UNA CADENA DE CARACTERES"
? luVal <= DATE()
? luVal = .T.
El valor devuelto en cada caso es realmente NULL. En otras palabras, cuando VFP pregunta cómo un NULL ( igual a "Yo no se") relaciona con algún otro valor, la respuesta siempre es NULL ( igual a "Yo no se"). Ahora puede estar pensando que esto es un ejemplo tonto, porque siempre puede verificar el tipo de variable (o campo) empleando las funciones TYPE() o VARTYPE().

Desafortunadamente TYPE() no está recomendada para verificar valores NULL, ya que devuelve un tipo "L" para una variable que haya sido inicializada como NULL, de esta forma:
luVal = NULL
? TYPE("luVal") && Devuelve "L"
Devolverá el tipo de dato original si la variable está contenida originalmente algún valor no nulo y luego adquiere un NULL, así:
lnTotal = 100
luVal = 10
? TYPE( "luVal" ) && Type = "N"
luVal = IIF( luVal > 20, luVal, NULL ) 
? TYPE( "luVal" ) && Value = NULL pero Type = "N"
lnTotal = lnTotal + luVal && lnTotal = NULL - ni siquiera 100!!!
¿Por qué es malo? Considere qué podría ocurrir si va a utilizar código que acumula valores dentro de un lazo (por ejemplo un SCAN) y un registro contiene un NULL. No sólo el total se pierde, sino que no va a recuperar los valores, porque tan pronto como alcance un valor nulo en el acumulador quedará NULL y todas las sumas subsecuentes van a resultar simplemente NULL. Puede que no reciba un error; pero no va a obtener el resultado deseado.

VARTYPE() tiene un comportamiento definitivamente mejor en este tema. De forma predeterminada devuelve un tipo de "X" si el valor verificado contiene NULL, sin embargo, al pasar el segundo parámetro como .T. puede obtener el tipo de dato original (en otras palabras, imita el comportamiento de TYPE()).

En la práctica, la mejor forma de controlar los nulos es verificarlos explícitamente empleando la función ISNULL() (que devuelve .T. si el valor verificado es nulo) o envolver expresiones que pueden contener nulos dentro de NVL(), de tal forma que cada valor nulo se reemplaza con un valor que sea más fácil de controlar. (De hecho, fue una omisión de la función NVL() lo que me ocasionó problemas esta semana y por tanto provocó este escrito.)

NVL() es simplemente una función que sustituye un valor especificado cuando se encuentra un NULL, así:
luVal = NULL
? NVL( luVal, 0 ) && Devuelve 0, no NULL!
Afortunadamente PUEDE utilizar funciones tales como SUM() y las funciones CALCULATE, o un SQL SUM(), incluso cuando hay valores NULL. Visual FoxPro es lo suficientemente inteligente como para ignorar esos valores - y esto incluso es uno de los beneficios de los valores NULL, al calcular promedios y contar registros - los NULLs se ignoran y no afectan el resultado.

Luego de haber dicho esto, ¿cuál es mi problema? Olvidé que cuando estamos concatenando cadenas recortadas (trimmed) de caracteres, uno de los cuales contiene un NULL, el resultado es NULL. La situación que tuve fue al llamar a un procedimiento almacenado en SQL Server que devolvía un valor en un cursor. Se suponía que el valor era una cadena que contenía hasta 6 caracteres espacios. Esto fue recortado, y provocó que se generara un nombre de archivo al concatenarlo con el ID de usuario que había iniciado sesión. Era algo así:
lcNextSeq = ALLTRIM( cur_result.cNextSeq ) && Este campo era NULL
lcFileName = lcUserID + lcNextSeq
El problema surgió cuando el procedimiento almacenado devolvió NULL, debido a que la operación de inserción previa había fallado y no tenía forma de atrapar el resultado en una función NVL(). No generaba un error; pero el resultado era NULL. Ahora, yo pienso que esto es un bug (quizás se introdujo en el producto desde VFP 6.0 al menos). Este código muestra por qué:
lcVal = ALLTRIM(NULL)
? lcVal && .NULL.
luVal = NULL
? luVal && .NULL.
? "Test" + lcVal && .NULL.
? "Test" + luVal && OPERATOR/OPERAND MISMATCH!!!!
Después de todo, si hay un error cuando una cadena de caracteres se concatena con una que contenga un NULL, entonces, simplemente aplicando una función ALLTRIM() debería no permitir que la operación se ejecute sin devolver error.

La moraleja de esta historia es, por supuesto, ¡ Controle siempre sus NULLs !

Incidentalmente puede estar pensando que como sólo utiliza datos VFP no se admiten nulos en sus tablas, este es el tipo de cosas que no le va a ocurrir. Está es lo cierto, mientras no utilice ni un JOIN en una consulta SQL. Recuerde que en ese tipo de consultas se devuelve un valor NULL como resultado si no hay coincidencia en los registros - independientemente de si sus tablas admiten nulos o no.

No hay comentarios. :

Publicar un comentario