INT vs INT UNSIGNED: Cómo optimizar el rango de tus datos
Descubre la diferencia técnica entre INT e INT UNSIGNED, cómo impacta el bit de signo en el rango de valores y por qué es una decisión crítica para tus Primary Keys.
En el diseño de bases de datos, cada byte cuenta. Sin embargo, a veces podemos ganar una capacidad inmensa sin añadir ni un solo bit de peso extra al disco. Uno de los ejemplos más claros y menos aprovechados por los principiantes es la elección entre un INT firmado y un INT UNSIGNED.
Aunque ambos ocupan exactamente 4 bytes (32 bits) en memoria, la forma en que interpretan esos bits cambia drásticamente el techo de crecimiento de tu aplicación. En este artículo, vamos a desglosar por qué deberías dejar de usar el INT por defecto y cuándo el “signo” puede convertirse en tu peor enemigo.
El problema del Bit de Signo
Para entender la diferencia, debemos bajar al nivel binario. Un INT estándar en la mayoría de los motores de bases de datos (como MySQL o SQL Server) utiliza 32 bits de almacenamiento.
En un INT normal (firmado), el primer bit a la izquierda, conocido como el Most Significant Bit (MSB), se reserva para indicar si el número es positivo o negativo.
- Si el bit es
0, el número es positivo. - Si el bit es
1, el número es negativo.
Esto significa que solo nos quedan 31 bits para representar la magnitud del número. Matemáticamente, esto nos da un rango de: $$[-2^{31}, 2^{31} - 1]$$
O lo que es lo mismo: de -2,147,483,648 a 2,147,483,647.
¿Qué pasa con el UNSIGNED?
Cuando declaramos un campo como UNSIGNED, le estamos diciendo al motor de la base de datos: “Olvida los números negativos, no los necesito”. Al hacer esto, liberamos ese bit de signo y lo sumamos a la capacidad de cómputo. Ahora tenemos los 32 bits completos para representar números positivos.
El nuevo rango es: $$[0, 2^{32} - 1]$$
Esto nos lleva hasta 4,294,967,295. ¡Hemos duplicado la capacidad máxima de la columna sin aumentar el tamaño del archivo de datos!
Comparativa de Rangos
A continuación, una tabla rápida para visualizar la diferencia de capacidad entre los tipos de enteros más comunes en SQL:
| Tipo de Dato | Bytes | Rango (Firmado) | Rango (Unsigned) |
|---|---|---|---|
| TINYINT | 1 | -128 a 127 | 0 a 255 |
| SMALLINT | 2 | -32,768 a 32,767 | 0 a 65,535 |
| MEDIUMINT | 3 | -8,388,608 a 8,388,607 | 0 a 16,777,215 |
| INT | 4 | ~2.1 Billones | ~4.2 Billones |
| BIGINT | 8 | ~9 Trillones | ~18 Trillones |
El famoso caso de “Gangnam Style” en YouTube
Si crees que 2 mil millones es un número “imposible de alcanzar”, recuerda lo que le pasó a YouTube en 2014. El contador de visitas del video “Gangnam Style” de PSY superó el límite de un INT de 32 bits firmado ($2^{31}-1$).
El sistema no estaba preparado para un número mayor a 2,147,483,647, lo que causó que el contador mostrara números negativos o se reiniciara. Google tuvo que actualizar urgentemente su infraestructura a BIGINT (64 bits) para soportar hasta 9 trillones de visitas. Si hubieran usado UNSIGNED desde el inicio, habrían tenido el doble de tiempo antes de enfrentar ese problema.
Implementación en SQL
Creación de tablas
En MySQL, definir un campo como UNSIGNED es sumamente sencillo. Es una práctica recomendada para casi todas las Primary Keys que sean auto-incrementales, ya que los IDs negativos rara vez tienen sentido.
CREATE TABLE usuarios (
-- Primary Key optimizada
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
edad TINYINT UNSIGNED -- Nadie tiene edad negativa
);
El peligro del “Underflow”
No todo es color de rosa. El uso de UNSIGNED introduce un riesgo: el error de Out of range. Si intentas realizar una operación aritmética que resulte en un número negativo en una columna UNSIGNED, el motor lanzará un error (en modo estricto) o producirá un valor inesperado.
-- Esto fallará si el campo 'puntos' es UNSIGNED y el valor actual es 0
UPDATE jugadores SET puntos = puntos - 1 WHERE id = 1;
-- Error: BIGINT UNSIGNED value is out of range
Compatibilidad entre Motores
Es importante notar que no todos los motores de bases de datos manejan UNSIGNED de la misma manera:
- MySQL / MariaDB: Soporte completo y nativo. Es muy común verlo en producción.
- PostgreSQL: No tiene un tipo UNSIGNED nativo. Para lograr un comportamiento similar, se deben usar
CHECK constraints:CREATE TABLE productos ( id SERIAL PRIMARY KEY, precio INT CHECK (precio >= 0) ); - SQLite: Ignora el atributo
UNSIGNEDen la mayoría de sus versiones, tratando todo como firmado.
Mejores Prácticas
- Primary Keys siempre UNSIGNED: A menos que tengas una razón arquitectónica muy extraña para usar IDs negativos, usa siempre
INT UNSIGNEDoBIGINT UNSIGNED. Ganarás el doble de espacio para IDs antes de tener que hacer una migración costosa. - Usa el tipo de dato más pequeño posible: No uses
INTsi unTINYINT UNSIGNED(hasta 255) o unSMALLINT UNSIGNED(hasta 65,535) es suficiente para ese campo (por ejemplo, para estados de pedido o categorías). - Cuidado con las restas: Si tu lógica de negocio implica que un valor pueda ser negativo (como un balance de cuenta bancaria o una temperatura), usa INT firmado. No fuerces el
UNSIGNEDdonde no pertenece. - Consistencia: Si tu Foreign Key es
INT UNSIGNED, la Primary Key a la que referencia debe ser exactamente del mismo tipo y longitud.
Conclusión
La diferencia entre INT e INT UNSIGNED no es solo estética; es una decisión de ingeniería que afecta la longevidad de tu esquema de datos. Elegir UNSIGNED para tus contadores e identificadores es una forma “gratuita” de escalar tu aplicación al doble de su capacidad inicial.
En un mundo donde los datos crecen exponencialmente, entender estos pequeños detalles es lo que separa a un desarrollador junior de uno que construye sistemas robustos.
¿Alguna vez te has quedado sin espacio en un campo INT? ¿Tuviste que migrar a BIGINT en una tabla con millones de registros? ¡Cuéntame tu experiencia en los comentarios!
¿Tienes un proyecto en mente?
Convierte tu idea en un producto real
Desarrollo web, aplicaciones a medida y consultoría tecnológica para empresas y startups. Cuéntame tu proyecto y te respondo en menos de 24 horas.