Cambiando el tipo de una columna: de caracter a numérico y viceversa

3 comentarios

Lo ideal es siempre que una vez que has definido la cantidad de columnas de cada tabla y el tipo de cada una de esas columnas ya no necesites modificarlas. Pero lo ideal … no siempre es lo que ocurre en la vida real.

Entonces, ahora te encuentras con una situación en la cual consideras conveniente o imprescindible cambiar el tipo de una columna. Por ejemplo era VARCHAR y la quieres convertir a INTEGER. O era SMALLINT y la quieres convertir a CHAR, cosas así.

El comando deseado

Si tienes una columna que es de tipo CHAR o VARCHAR y la quieres cambiar a INTEGER muchos desearían que existiera algo similar a:

ALTER TABLE
   MiTabla
ALTER COLUMN
   MiColumna
TYPE
   INTEGER

Pero tal comando no existe en Firebird, si lo intentas obtendrás el mensaje: “Cannot change datatype for column MiColumna from a character type to a non-character type.

¿Por qué, cuál es el problema? El problema es que Firebird no hace la conversión en el momento en que escribes un comando como el anterior sino en el momento en que consultas su valor (con un SELECT) o cambias su valor (con un UPDATE). Por lo tanto cuando escribes el comando ALTER TABLE él no puede saber si todos los valores pueden convertirse a INTEGER o no. Y entonces hace lo más seguro: rechaza el cambio.

Claro, tú puedes decir: “pero si solamente hay números enteros en esa columna, debería aceptar el cambio del tipo de datos”. Eso tú lo sabes, pero el Firebird no lo sabe porque no verifica que efectivamente así sea. ¿Y por qué no lo verifica? Porque tu tabla podría tener millones y millones de filas y verificarla podría demorar mucho tiempo, por lo tanto hace la sencilla: impide que modifiques el tipo de datos.

La solución

Ya hemos visto cual es el problema, ahora veremos que hay una solución. No es tan sencilla como escribir un comando ALTER TABLE pero funciona.

Paso 1. Verificar que todos los valores puedan ser convertidos a INTEGER

Evidentemente que el 100% de los valores deben poder convertirse a INTEGER, si hay al menos uno que no puede convertirse entonces primero hay que solucionar eso y luego continuar con los siguientes pasos.

SELECT
   CAST(MiColumna AS INTEGER) AS TodosSonNumerosEnteros
FROM
   MiTabla;

Si el SELECT anterior terminó con errores, y quieres ver cuales son las filas que tienen esos errores (o sea, las filas que no tienen números enteros) el siguiente artículo te muestra lo que puedes hacer para ver a esas filas problemáticas:

Validando que el contenido de un CHAR o VARCHAR sea numérico

Si el SELECT anterior terminó sin errores, entonces seguimos con el:

Paso 2. Renombrar la vieja columna

COMMIT;

ALTER TABLE
   MiTabla
ALTER COLUMN
   MiColumna
TO
   ColumnaVieja;

Si esta columna tiene dependencias (es decir, si ha sido usada en alguna vista, stored procedure o trigger) entonces obtendrás un mensaje de error. Debes solucionar eso antes de poder continuar.

Este paso es muy importante porque te permite descubrir si la columna tiene dependencias.

Paso 3. Agregarle una columna a la tabla con el tipo de datos deseado

COMMIT;

ALTER TABLE
   MiTabla
ADD
   MiNuevaColumna INTEGER;

Paso 4. Agregarle los valores a la nueva columna

COMMIT;

UPDATE
   MiTabla
SET
   MiNuevaColumna = CAST(ColumnaVieja AS INTEGER)
WHERE
   ColumnaVieja IS NOT NULL;

Paso 5. Verificar que la nueva columna tenga los valores correctos

COMMIT;

SELECT
   (MiNuevaColumna + 1)
FROM
   MiTabla;

Si el Firebird te muestra algún mensaje de error (supuestamente todo debería estar ok, pero siempre lo mejor es asegurarse de que así sea) entonces tienes que buscar el motivo y solucionarlo, antes de continuar.

Paso 6. Borrar la columna vieja

Si el SELECT anterior finalizó sin errores, entonces ya podemos borrar con toda seguridad a la columna vieja, porque ya no la necesitaremos más.

COMMIT;

ALTER TABLE
   MiTabla
DROP COLUMN
   ColumnaVieja;

COMMIT;

Este paso es muy conveniente hacerlo para no confundirnos. Si no borramos a la columna vieja más adelante podríamos estar asignándole valores, algo que muy probablemente ya no deberíamos hacer.

Paso 7 (opcional). Renombrar a la nueva columna

Si deseas que la nueva columna tenga el mismo nombre que tenía la columna vieja, entonces:

ALTER TABLE
   MiTabla
ALTER COLUMN
   MiNuevaColumna
TO
   MiColumna

COMMIT;

Paso 8 (opcional). Poner a la nueva columna en la misma posición que tenía la vieja columna

Si quieres que la nueva columna que acabas de crear se encuentre en la misma posición que tenía la columna vieja que acabas de borrar, entonces:

ALTER TABLE
   MiTabla
ALTER
   MiColumna
POSITION
   6;

COMMIT;

Si la vieja columna se encontraba en la posición 1, entonces después de POSITION escribirías 1. Si se encontraba en la posición 2, escribirías 2, y así sucesivamente. El 6 que se puso arriba es solamente un ejemplo.

Conclusión:

En Firebird no hay un comando que nos permita cambiar el tipo de datos de una columna. Y no lo hay por nuestra propia seguridad, de esa manera se evita que cometamos errores graves como convertir texto a INTEGER.

Sin embargo, si necesitamos hacerlo hay formas de conseguirlo. En este artículo se mostró la forma más segura de todas (no la única, pero sí la más segura y conveniente de utilizar).

Con el Paso 1. verificamos que todos los números sean enteros. Con el Paso 2. verificamos que no hay dependencias. Con el Paso 3. agregamos una columna que tiene el tipo de datos que deseamos tener. Con el Paso 4. le agregamos a la nueva columna los valores que debe tener. Con el Paso 5. verificamos que la nueva columna tenga los valores correctos. Con el Paso 6. borramos a la vieja columna, esto nos evitará confusiones más adelante. Con el Paso 7. renombramos a la nueva columna, para que se llame igual a la columna original. Con el Paso 8. ponemos a la nueva columna en la misma posición que tenía la columna original.

Siguiendo estos pasos te asegurarás que todo se ha realizado correctamente. Todo quedará como debe quedar.

Por lo tanto, si alguna vez necesitas cambiar el tipo de datos de una columna, ya sabes como lograrlo.

Artículos relacionados:

Validando que el contenido de un CHAR o VARCHAR sea numérico

El índice del blog Firebird21

El foro del blog Firebird21

¿CHAR o VARCHAR? ¿cuál es mejor?

4 comentarios

Cuando declaras una columna alfanumérica puedes elegir entre CHAR y VARCHAR. ¿Cuál de esos tipos de datos es mejor?

Guardando columnas CHAR en .DBF y en Paradox

En las tablas .DBF y en las tablas de Paradox, si declaras una columna como CHAR y guardas en esa columna menos caracteres que los declarados se la completa con espacios en blanco.

Ejemplo:

Si una columna de una tabla .DBF está declarada como CHAR(20) y guardas en esa columna:

‘HOLA’, se le agregan 16 espacios en blanco, para completar los 20 declarados

‘ASUNCIÓN’, se le agregan 12 espacios en blanco para guardar los 20 declarados

‘AMERICA’ se le agregan 13 espacios en blanco para completar los 20 declarados

Guardando columnas CHAR en Firebird

Firebird no agrega espacios en blanco al final porque cuando se guarda una fila, la fila completa (con todas sus columnas CHAR, VARCHAR, INTEGER, DATE, TIME, etc.) es comprimida usando el algoritmo RLE.

Se hace así para ahorrar espacio en el disco duro porque la fila comprimida ocupa menos espacio que la fila sin comprimir.

Ejemplo:

Una columna de una tabla Firebird está declarada como CHAR(20) y guardas en esa columna:

‘HOLA’, ocupa 4 caracteres

‘ASUNCIÓN’, ocupa 8 caracteres

‘AMÉRICA’, ocupa 7 caracteres

Como puedes ver Firebird solamente guarda los datos significativos, no agrega espacios en blanco al final.

Guardando columnas VARCHAR en Firebird

Las columnas de tipo VARCHAR ocupan 2 bytes más que los declarados porque se usan 2 bytes para conocer la longitud de la cadena guardada.

Ejemplo:

Una columna de una tabla Firebird está declarada como VARCHAR(20) y guardas en esa columna:

‘HOLA’, ocupa 6 caracteres (2 que indican la longitud más 4 de la palabra ‘HOLA’)

‘ASUNCIÓN’, ocupa 10 caracteres (2 que indican la longitud más 8 de la palabra ‘ASUNCIÓN’)

‘AMÉRICA’, ocupa 9 caracteres (2 que indican la longitud más 7 de la palabra ‘AMÉRICA’)

Ahorrando espacio en el disco duro

Si lo que necesitas es ahorrar espacio en el disco duro entonces CHAR es más eficiente que VARCHAR porque como has visto cada columna CHAR ocupa 2 bytes menos que su correspondiente columna VARCHAR.

Espacio ocupado en la memoria RAM

En ambos casos la columna ocupa el espacio declarado.

Ejemplo:

Una columna CHAR(20) ocupa 20 bytes en la memoria RAM

Una columna VARCHAR(20) ocupa 20 bytes en la memoria RAM

Consultando columnas CHAR y VARCHAR

Cuando ejecutas un comando SELECT sobre una columna CHAR Firebird le agrega los espacios en blanco necesarios para completar la longitud declarada.

Cuando ejecutas un comando SELECT sobre una columna VARCHAR Firebird ignora a los dos primeros caracteres (los que indican la longitud) y devuelve el resto de la cadena.

Por lo tanto, al usar CHAR se ahorran 2 bytes pero al hacer el SELECT de esa columna se pierde tiempo en rellenarla con los espacios en blanco faltantes.

Decidiendo entre CHAR y VARCHAR

Como hemos visto, al declarar una columna como CHAR se ahorra espacio en el disco duro (2 bytes menos que si la declaramos como VARCHAR) pero las consultas que involucran a columnas CHAR son más lentas que las que involucran a columnas VARCHAR porque las columnas CHAR deben ser rellenadas con espacios en blanco antes de ser mostradas.

Esos 2 bytes que se ahorran al declarar a la columna como CHAR, en esta época y con los discos duros gigantescos disponibles, pueden ser irrelevantes y no degradarán el rendimiento de la Base de Datos. La excepción es cuando la cantidad de caracteres a almacenar es pequeña, por ejemplo declarar una columna como VARCHAR(3) es un error porque en realidad se guardan en el disco duro 5 bytes (2 de la longitud más 3 declarados)  cuando usando CHAR(3) solamente necesitaríamos 3 bytes y además al ser la longitud tan pequeña (sólo 3 bytes) VARCHAR no sería más rápido que CHAR.

Por lo tanto:

  • Si la cantidad de caracteres a guardar en una columna es fija y menor que 80, usar CHAR
  • Si la cantidad de caracteres a guardar en una columna es fija o es variable y esa cantidad es menor o igual que 10, usar CHAR
  • Si la cantidad de caracteres a guardar en una columna es variable y es mayor que 10, usar VARCHAR
  • Si la cantidad de caracteres a guardar en una columna es variable y mayor que 10.000 suele ser preferible usar un BLOB de texto

Ejemplos donde se debería usar CHAR

  • Guardar el código del Estado (‘AK’, ‘AZ’, ‘NY’, ‘SC’, ‘TX’). Todos los códigos tienen 2 caracteres
  • Guardar el sexo (‘F’, ‘M’). Todos tienen 1 caracter
  • Guardar el prefijo telefónico (‘021’, ‘022’, ‘028’, ‘0293’). Todos los prefijos tienen 3 caracteres ó 4 caracteres

Ejemplos donde se debería usar VARCHAR

  • Guardar el nombre de la persona (‘ANA PAULA’, ‘CYNTHIA ELIZABETH’, ‘PATRICIA ADRIANA’). La cantidad de caracteres es variable
  • Guardar la dirección del proveedor (‘COLÓN 1234’, ‘HERNANDARIAS 3455’, ‘ESTADOS UNIDOS 56789’). La cantidad de caracteres es variable

Artículo relacionado:

El índice del blog Firebird21