Tipos de datos de PostgreSQL en profundidad
El sistema de tipos de PostgreSQL no se trata solo de validar la entrada; también determina cómo se almacena un valor en disco, qué operadores y tipos de índice pueden acelerar las consultas contra él y cuánta conversión implícita ocurre antes de que se ejecute una comparación.
Esta página es el mapa conceptual para el resto de la sección data-types-jsonb, que cubre cómo PostgreSQL categoriza los tipos, cómo el almacenamiento y la indexación se derivan de esa elección, y cuándo elegir jsonb, arrays, enums o dominios en lugar de una columna estrictamente normalizada.
Resumen
- El tipo de una columna en PostgreSQL determina su formato de almacenamiento, sus operadores válidos y qué métodos de acceso de índice pueden acelerarlo, no solo qué valores acepta.
- Por qué Importa: Elegir la categoría de tipo incorrecta puede deshabilitar silenciosamente la indexación, desperdiciar almacenamiento o hacer que las restricciones sean imposibles de aplicar.
- Conceptos Clave: familia de tipos, TOAST, clase de operador, métodos de acceso GIN/GiST/B-tree,
jsonvsjsonb. - Cuándo Usar: Utilice este modelo al diseñar las columnas de una nueva tabla, o cuando una consulta que "debería" usar un índice esté realizando un escaneo secuencial en su lugar.
- Limitaciones / Compensaciones: Los tipos flexibles como
jsonbintercambian solidez de restricciones y precisión de índice por flexibilidad de esquema. - Temas Relacionados: tipos numéricos y de texto, arrays y tipos compuestos, tipos UUID/enum/dominio, tipos de fecha y marca de tiempo.
Fundamentos
Cada columna en PostgreSQL tiene exactamente un tipo declarado, y ese tipo restringe qué valores son válidos, cómo se comparan y cómo se ordenan.
PostgreSQL agrupa sus tipos integrados en familias: numéricos, de caracteres, booleanos, de fecha/hora, UUID, binarios, geométricos, de red y los tipos semiestructurados json y jsonb.
Más allá de los tipos escalares integrados, PostgreSQL también soporta tipos compuestos construidos a partir de otros tipos, tipos de array que contienen múltiplos de cualquier tipo, tipos de rango que representan un intervalo de valores y tipos enum para conjuntos cerrados de etiquetas.
Un dominio es una restricción definida por el usuario superpuesta a un tipo base existente, lo que le permite nombrar y reutilizar una regla de validación como "una dirección de correo electrónico válida" en muchas columnas.
Elegir bigint en lugar de int, o numeric en lugar de float, no es una preferencia estilística; cambia el comportamiento de desbordamiento, la precisión aritmética y el ancho del índice.
La regla general es elegir el tipo más pequeño y específico que represente correctamente los datos, ya que los tipos más estrechos producen índices más pequeños y detectan datos inválidos antes.
text y varchar(n) se comportan de manera idéntica en PostgreSQL, aparte de la verificación de longitud, a diferencia de otras bases de datos donde varchar conlleva una diferencia real de almacenamiento o rendimiento.
jsonb almacena JSON en un formato binario descompuesto en lugar de texto, que es lo que lo diferencia del tipo json simple.
Mecánicas e Interacciones
PostgreSQL almacena la mayoría de los datos de fila en campos de tamaño fijo o longitud variable empaquetados en páginas de 8 kilobytes, y cualquier valor único mayor de aproximadamente 2 kilobytes se mueve fuera de línea a una tabla lateral llamada TOAST.
TOAST es la razón por la que un documento jsonb grande o un valor de texto largo no hincha cada escaneo secuencial de la tabla principal, ya que el valor sobredimensionado solo se recupera cuando una consulta realmente lo necesita.
Cada tipo pertenece a una o más clases de operador, y un índice solo puede acelerar los operadores que su método de acceso soporta para ese tipo, razón por la cual no todos los tipos funcionan con todos los tipos de índice.
Los índices B-tree soportan comparaciones de igualdad y ordenación y funcionan con la mayoría de los tipos escalares, mientras que los índices GIN soportan operadores de contención y existencia y son los que hacen rápidas las consultas jsonb y de arrays.
Los índices GiST y SP-GiST soportan operadores de estilo geométrico y de superposición de rangos, razón por la cual PostGIS y los tipos de rango dependen de ellos en lugar de B-tree.
Las conversiones implícitas y explícitas cambian el tipo de un valor antes de la comparación, y una conversión aplicada a una columna indexada en una cláusula WHERE puede impedir silenciosamente que el planificador use ese índice.
-- Existe un índice en created_at (timestamptz)
CREATE INDEX events_created_at_idx ON app.events (created_at);
-- Sargable: el planificador puede usar el índice
SELECT id FROM app.events WHERE created_at > now() - interval '1 day';
-- No sargable: la conversión envuelve la columna, el índice es inutilizable
SELECT id FROM app.events WHERE created_at::date = current_date;La segunda consulta parece equivalente a un humano, pero la conversión en created_at obliga a PostgreSQL a evaluar una expresión para cada fila en lugar de buscar en el índice.
jsonb también descarta el orden de las claves, los espacios en blanco y las claves duplicadas durante su descomposición, lo que lo hace más rápido de consultar pero significa que no puede reproducir el texto JSON original exacto de la manera que lo hace json.
Los tipos Enum almacenan sus valores como pequeños enteros internamente mientras muestran la etiqueta que usted definió, proporcionando un almacenamiento compacto con una salida legible.
Consideraciones Avanzadas y Aplicaciones
Decidir entre jsonb y columnas normalizadas es en realidad una decisión sobre cuán estable es el esquema y cómo se consultarán los datos.
Los campos por los que filtra, une o necesita restricciones pertenecen a columnas tipificadas reales, mientras que los atributos genuinamente variables o dispersos son un ajuste razonable para jsonb.
Un índice GIN en una columna jsonb acelera las consultas de contención con el operador @>, pero no reemplaza a un índice B-tree para búsquedas de igualdad en una clave única y bien conocida, que generalmente requiere un índice de expresión en su lugar.
Los tipos de rango le permiten modelar un intervalo, como la ventana de tiempo de una reserva, como un solo valor con operadores de superposición integrados, lo que es más correcto y a menudo más rápido que comparar manualmente un par de columnas de inicio/fin.
Los tipos Enum son rápidos y auto-documentados, pero agregar o eliminar un valor requiere una migración de esquema, lo que hace que una tabla de búsqueda sea una mejor opción cuando el conjunto de valores cambia con frecuencia.
Los dominios centralizan una regla de validación en un solo lugar, pero los errores hacen referencia al nombre del dominio en lugar de a la columna, lo que algunos equipos encuentran menos legible de inmediato en el manejo de errores de la aplicación.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Columnas normalizadas | Restricciones fuertes, mejor soporte de índices y uniones | Rígido, necesita una migración para cada nuevo campo | Atributos estables y conocidos consultados directamente |
| Columna jsonb | Esquema flexible, agrupa atributos dispersos o variables | Restricciones más débiles, el índice GIN solo cubre consultas de tipo contención | Bolsas de atributos semiestructurados o consultados raramente |
| Tipo Enum | Almacenamiento compacto, valores auto-documentados | Agregar un valor requiere una migración | Conjuntos cerrados pequeños y que cambian raramente |
| Tabla de búsqueda (clave externa) | Valores modificables sin migración, soporta metadatos por valor | Una unión adicional por cada lectura | Conjuntos que cambian a menudo o necesitan atributos adicionales por valor |
Ninguno de estos enfoques es mutuamente excluyente dentro de un esquema, y la mayoría de los diseños reales mezclan columnas normalizadas, un comodín jsonb y un par de conjuntos de enum o tablas de búsqueda, dependiendo del campo.
Conceptos Erróneos Comunes
jsonbno tiene esquema, por lo tanto no tiene costo -jsonbtodavía tiene costos de almacenamiento e indexación, y reemplazar todas las columnas tipificadas porjsonbhace que las restricciones y las uniones sean más difíciles de aplicar.varchar(n)es más rápido quetext- en PostgreSQL usan el mismo almacenamiento subyacente yvarchar(n)solo agrega una verificación de longitud al escribir.- El punto flotante está bien para el dinero siempre que se redondee cuidadosamente - el punto flotante no puede representar la mayoría de las fracciones decimales exactamente, por lo que
numerices el tipo correcto para la moneda independientemente de la disciplina de redondeo. - Cualquier índice puede acelerar cualquier consulta en una columna - un índice solo acelera los operadores que su método de acceso realmente soporta para ese tipo, razón por la cual la contención de
jsonbnecesita GIN, no B-tree. jsonyjsonbson básicamente el mismo tipo con un nombre diferente -jsonalmacena el texto original exacto mientras quejsonbalmacena una forma binaria descompuesta, y solojsonbsoporta operadores de indexación como la contención.
Preguntas Frecuentes
¿Cuándo debería usar jsonb en lugar de columnas tipificadas separadas?
Utilice jsonb cuando el conjunto de atributos sea genuinamente variable o disperso y rara vez necesite filtrar, unir o restringir campos individuales, mientras que los campos estables y consultados con frecuencia pertenecen a columnas tipificadas en su lugar.
¿Cuál es la diferencia real entre json y jsonb?
json almacena el texto de entrada exacto, preservando el orden de las claves y los espacios en blanco.
jsonb descompone el valor en un formato binario, lo que descarta ese formato pero habilita la indexación y consultas de contención más rápidas.
¿`varchar(n)` tiene un rendimiento mejor que `text` en PostgreSQL?
No, ya que ambos utilizan la misma representación de almacenamiento y varchar(n) solo agrega una verificación de longitud adicional cuando se escribe un valor.
¿Por qué mi índice no acelera una consulta que filtra un campo jsonb?
- Un índice B-tree en toda la columna
jsonbno acelera las búsquedas a nivel de clave. - Las consultas de contención con
@>necesitan un índice GIN en su lugar. - Filtrar una clave única y bien conocida generalmente requiere un índice de expresión en esa clave, no un índice de columna completa.
¿Qué es TOAST y cuándo entra en juego?
TOAST mueve los valores mayores de aproximadamente 2 kilobytes fuera de la fila de la tabla principal a una tabla lateral, de modo que los valores jsonb o de texto anchos no inflen cada escaneo secuencial de la tabla.
¿Por qué se prefiere `numeric` sobre `float` para el dinero?
numeric almacena dígitos decimales exactos, mientras que los tipos de punto flotante aproximan los valores en binario y no pueden representar la mayoría de las fracciones decimales exactamente, lo que introduce errores de redondeo en sumas y comparaciones.
¿Cuál es la diferencia entre un tipo enum y una tabla de búsqueda?
Un enum es compacto y auto-documentado, pero requiere una migración para agregar un valor, mientras que una tabla de búsqueda con una clave externa puede tener nuevos valores sin migración y puede contener metadatos adicionales por valor.
¿Cómo ayudan los tipos de rango en comparación con las columnas de inicio/fin separadas?
Un tipo de rango representa un intervalo como un solo valor con operadores de superposición y contención integrados, lo que es más correcto y a menudo más rápido que comparar dos columnas separadas manualmente.
¿Puede una conversión en una columna romper el uso del índice?
Sí, ya que envolver una columna indexada en una conversión o función, como created_at::date, obliga a PostgreSQL a evaluar esa expresión por fila en lugar de buscar en el índice de la columna sin procesar.
¿Qué es un dominio y en qué se diferencia de una restricción CHECK en una columna?
Un dominio envuelve un tipo base con una restricción reutilizable que usted define una vez y aplica a muchas columnas, de modo que la regla y su mensaje de error viven en un solo lugar en lugar de repetirse en cada columna.
¿Los arrays reemplazan la necesidad de una tabla de unión?
No usualmente, ya que los arrays funcionan bien para listas pequeñas y desnormalizadas como etiquetas, pero una vez que los elementos del array necesitan sus propios atributos o relaciones, una tabla de unión adecuada es el mejor ajuste.
¿Por qué algunos tipos necesitan índices GIN o GiST en lugar de B-tree?
Porque B-tree solo soporta operadores de igualdad y ordenación, mientras que la contención (jsonb, arrays) necesita GIN y los operadores geométricos o de superposición de rangos necesitan GiST o SP-GiST, ya que estos no son ordenables como lo requiere B-tree.
Relacionados
- Fundamentos de Tipos de Datos - recorrido práctico de los tipos principales
- Operadores e Indexación de JSONB - consultas de contención e índices GIN en la práctica
- Tipos Numéricos y de Texto - precisión, escala y opciones de tipos de cadena
- Arrays y Tipos Compuestos - tipos de colección y columnas estructuradas
- Tipos UUID, Enum y Dominio - identificadores, conjuntos cerrados y restricciones reutilizables
- Tipos de Fecha, Hora y Timestamptz - opciones de tipos temporales
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, y PostGIS 3.5+.