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:
Jorge Traslaviña
Nov 24, 2016 @ 12:45:06
Hola Walter … saludos..
Tengo una duda y seguramente usted es el indicado para ayudarme..
varchar(50) y varchar(500) me permiten guardar como máximo 50 y 500 caracteres respectivamente, eso es claro.
El asunto de mi duda es, para el caso en que se manejan datos de diversas longitudes, en diversas columnas, de diversos archivos, (ej. en un archivo tenemos columnas de 40, 60, 100, en otro columnas de 35, 70, 60, 120, en otro columnas de 110, 70, 55, etc), si se trabaja preferiblemente con dominios como es mi caso se necesitan generar muchos dominios, digamos uno por longitud esperada, con la consiguiente generación de confusión y aumento del trabajo de definición de campos y procedimientos, sobre todo cuando tenemos una bastante cantidad de archivos y campos a definir (20, 50, 100… seguramente cada uno tendrá su parámetro de bastante…).
La pregunta es si es posible declarar un solo dominio varchar(xxx) y englobar todo dentro de este dominio, es decir en la definición de dominios y archivos en lugar de tener algo como
dom_a varchar(50)
dom_b varchar(65)
dom_c varchar(500)
…
n dominio…
y tabla ejemplo como
col_a dom_a
col_b dom_b
col_c dom_c
tener
un dominio
dom_d varchar(500)
y tabla ejemplo
col_a dom_d
col_b dom_d
col_c dom_d
Se estaría esquivando alguna regla de la correcta definición y normalización de la base …?
Sería una salida correcta para disminuir confusión en la definición de campos de tablas y procedimientos ?
En que se afectaría el espacio de almacenamiento y sobretodo el rendimiento de las operaciones sobre la base de datos ?
Gracias por su excelente blog, quedo pendiente de sus comentarios.
Saludos
wrov
Nov 25, 2016 @ 02:08:59
Hola
En Firebird, el máximo valor que se puede asignar a una columna de tipo VARCHAR es de 32765. Si se necesitan más caracteres hay que usar o varias columnas o una columna de tipo BLOB.
Antes de guardar una fila, toda la fila es comprimida mediante un algoritmo llamado RLE. ¿Qué implica eso? que para guardar a las palabras «HOY ES VIERNES» no importa si defines a la columna como VARCHAR(16), VARCHAR(200), VARCHAR(15000), porque en todos los casos la cantidad de bytes que ocupará en el disco duro será exactamente la misma.
Pero el asunto cambia cuando el contenido de esa fila que estaba en el disco duro se copia en la memoria RAM (por ejemplo, cuando escribes el comando SELECT). Allí sí se usa el valor que hubieras definido. O sea que si definiste a la columna como VARCHAR(15000) las palabras «HOY ES VIERNES» ocuparán 15.000 bytes de tu memoria RAM. Y claro, eso parece una exageración.
Sin embargo, con las computadoras modernas eso generalmente no es un problema porque la memoria RAM que tienen es gigantesca.
De todas maneras, siempre es buena práctica no desperdiciar memoria.
En mi caso particular defino varios dominios para guardar caracteres: D_NOMBRE_08, D_NOMBRE_15, D_NOMBRE_20, D_NOMBRE_40, D_NOMBRE_80, D_NOMBRE_128, etc.
Tengo la costumbre de empezar los nombres de mis dominios con D_, así de un rápido vistazo puedo saber que se trata de un dominio (análogamente los nombres de las vistas empiezan con V_, los nombres de los stored procedures con SP_, etc.)
En conclusión, sí puedes usar un único dominio D_NOMBRE definido como VARCHAR(32765) para todas las columnas que guarden caracteres en todas tus tablas, aunque en ese caso estarás desperdiciando un montón de memoria RAM. Lo cual quizás ni siquiera notes porque las computadoras modernas tienen muchísima RAM.
Saludos.
Walter.
Gustavo Ramos
Ene 29, 2017 @ 05:24:10
Hola Walter.
Gracias por todo lo que haces.
Mi consulta es, y en un campo varchar(40) que se graba mayoritariamente con NULL, qué tamaño ocupa en disco y en RAM esos registros?
Gracias.
wrov
Ene 29, 2017 @ 13:11:44
Si el contenido de una columna es NULL, en el disco esa columna se guarda con 1 bit, si no es NULL depende del tamaño de toda la fila, porque la compresión se hace a toda la fila, no solamente a una columna.
En memoria RAM siempre ocupa el tamaño declarado, en este caso sería de 40 bytes.
Saludos.
Walter.
Adalberto Pérez Reyes
May 16, 2021 @ 12:56:10
Estimado Wrov, un campo varchar que maneja claves encriptadas, necesito que en el select me devuelva el contenido con el formato que puedo ver con el editor unicode y no texto plano
wrov
May 23, 2021 @ 17:08:58
Hola
No sé si te refieres a esto, pero en el SELECT tú puedes especificar el conjunto de caracteres válido escribiendo algo como:
SELECT MiColumna1 COLLATE ISO8859_1 FROM MiTabla
Donde desde luego puedes cambiar el ISO8859_1 por cualquier otro conjunto de caracteres.
Saludos.
Walter.
Adalberto Perez
May 24, 2021 @ 17:03:42
Gracias por tu respuesta, justamente estaba usando esa secuencia en el select, pero para un campo encriptado (probé varios algoritmos y con todos me resulta igual) no me devuelve el contenido tal cual lo envíe, me cambia el formato y por mas que pruebo no encuentro un set de caracteres, que me conserve el formato en la devolución.