El problema con ASCII_CHAR(0)

4 comentarios

En el Lenguaje C y sus derivados una cadena alfanumérica finaliza cuando se encuentra un carácter cuyo código ASCII es cero.

Firebird está escrito en el Lenguaje C++ que es un derivado del Lenguaje C entonces también para él una cadena finaliza cuando se encuentra un carácter cuyo código ASCII es cero.

Ejemplo:

INSERT INTO BANCOS
           (BAN_IDENTI, BAN_NOMBRE)
    VALUES (0, 'BANCO CENTRAL')

INSERT INTO BANCOS
           (BAN_IDENTI, BAN_NOMBRE)
    VALUES (0, 'BANCO ' || ASCII_CHAR(0) || ' NACIONAL')

Si ahora terminamos la transacción con un COMMIT y luego consultamos la tabla BANCOS esto es lo que veremos:

ASCII1

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

La primera fila está correcta, pero la segunda fila no. Como entre las palabras ‘BANCO’ y ‘NACIONAL’ se introdujo un carácter ASCII con el código cero entonces no se puede ver lo que se escribió a continuación de la palabra ‘BANCO’.

Para verificar que el problema lo tenemos porque hay un carácter que tiene el código ASCII cero, escribimos esta consulta:

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_NOMBRE CONTAINING ASCII_CHAR(0)

Y este es el resultado que obtenemos:

ASCII2

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

 Donde como puedes ver nos muestra que en la columna BAN_NOMBRE de la segunda fila hay al menos un carácter con el código ASCII de cero.

Solucionando el problema

Afortunadamente este problema tiene una solución muy sencilla y es escribir un comando similar al siguiente:

UPDATE
   BANCOS
SET
   BAN_NOMBRE = REPLACE(BAN_NOMBRE, ASCII_CHAR(0), '')

Donde lo que hacemos es reemplazar el carácter problemático por otro carácter; en este caso por un carácter vacío.

Para verificar que nuestra actualización haya funcionado escribimos esta consulta:

SELECT
   *
FROM
   BANCOS

Y este es el resultado que obtenemos:

ASCII3

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

Y sí, funcionó. ¡¡¡Perfecto!!!

Ahora sí vemos los nombres completos de los dos Bancos.

El problema con los índices

Como ya hemos visto, si hay un carácter que tiene el código ASCII cero entonces eso impide que veamos los caracteres que se encuentran a continuación, pero ese no es el único problema que podemos tener.

Otro problema es que los índices que creemos en una columna que tiene caracteres ASCII cuyo código es cero, se corromperán.

Por lo tanto, si descubres que un índice está corrupto y no sabes el motivo, lo que puedes hacer es verificar si en esa columna hay algunos caracteres que tienen código ASCII cero. Si ese llegara a ser el caso entonces deberás eliminar todos esos caracteres problemáticos, de la forma mostrada más arriba. Así se solucionará el problema.

Un truco de protección:

Si queremos proteger el contenido de nuestras columnas y no queremos encriptarlas entonces podemos usar este truco: colocamos un carácter cuyo código ASCII es cero en alguna parte de la columna y todo lo que esté a continuación no será mostrado en los SELECTs. Es una protección sencilla pero muy efectiva ante quienes no dominan Firebird ni son hackers profesionales.

Conclusión:

Si no vemos todo el contenido de una columna puede ser que el problema se deba a que tiene insertados algunos caracteres cuyo código ASCII es cero. Esto es mucho más probable que ocurra cuando el contenido de esas columnas proviene de fuentes externas, por ejemplo de tablas .DBF, archivos de texto, tablas de Access, etc.

La solución es reemplazar a esos caracteres que tienen código ASCII cero con algún otro carácter, por ejemplo con el espacio vacío que se obtiene escribiendo dos apóstrofos, uno a continuación del otro.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

Tratamiento de columnas NULL

8 comentarios

En el lenguaje SQL la palabra NULL significa “desconocida/o”.

Cuando establecemos que una columna de una tabla puede ser de tipo NULL lo que le estamos diciendo al Firebird es: “en esta columna podríamos tener algunos valores desconocidos”.

¿Establecer una columna como NULL podría causarnos problemas?

Pues sí, si es que nuestra aplicación no maneja correctamente a los NULL.

Entender como tratar a las columnas que pueden contener NULL es fundamental para que nuestra aplicación sea exitosa. En general, como una regla mientras no domines SQL:

  • No debes establecer NULL para las columnas numéricas, en lugar de eso ponerles como valor por defecto el número 0 (cero)
  • No debes establecer NULL para las columnas alfanuméricas, en lugar de eso ponerles como valor por defecto un espacio en blanco
  • No debes establecer NULL para las columnas de tipo fecha, en lugar de eso ponerles como valor por defecto una fecha muy antigua y que nunca usarás, como: ’01/01/1900′

¿Por qué es preferible ponerles valores por defecto a las columnas?

Porque el tratamiento de los valores de esas columnas se facilita. Por ejemplo, la función agrupada AVG() ignora a las columnas que tienen NULL para hallar el promedio. Y en general vas a querer que no las ignore. Además, en tu aplicación podrías olvidarte que una columna puede contener NULL y perderías tiempo buscando el problema, problema que nunca habrías tenido si la columna tuviera un valor por defecto.

Dependiendo de nuestro lenguaje de programación podemos hacer asignaciones NULL y también hacer comparaciones para saber si una columna es NULL o no lo es.

En el lenguaje Visual FoxPro escribiríamos:

lcNombre = NULL          && le asigna NULL a la variable lcNombre
IF ISNULL(lcNombre)      && si la variable lcNombre es NULL, entonces ...
IF !ISNULL(lcNombre)     && si la variable lcNombre no es NULL, entonces...

Tener columnas de tipo NULL es muy útil siempre y cuando sepas como usarlas. Si ese no es el caso, es mejor evitarlas y ponerles valores por defecto a todas las columnas, vivirás más tranquilo así.

Si tienes columnas NULL, en tu aplicación a cada momento podrías tener que verificar si una columna es NULL o no lo es. Y eso te hará perder tiempo y complicará tu código fuente.

Una forma de evitar ese problema es que en tu Base de Datos uses la función COALESCE(), como se muestra a continuación:

SELECT
   COALESCE(MiColumnaNumérica, 0) AS MiNumero
FROM
   MiTabla

La función COALESCE() devuelve el primer argumento que no sea NULL. O sea que en este ejemplo si MiColumnaNumérica no es NULL, ese es el valor que devolverá. Y si MiColumnaNumérica es NULL devolverá 0.

.