Arrays frente a tablas normalizadas
Cuándo los arrays son la opción adecuada
Arrays frente a tablas normalizadas es una lección gratuita de SQL Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de SQL Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de SQL Academy incluye 4 lecciones en total.
Dos formas de almacenar varios valores
Cuando una sola fila necesita contener varios valores relacionados, PostgreSQL ofrece dos enfoques principales: almacenarlos como una columna de tipo array en la misma fila o crear una tabla hija independiente en la que cada valor ocupe su propia fila.
Comprender cuándo utilizar cada enfoque es una habilidad fundamental para diseñar bases de datos eficientes y fáciles de mantener.
El enfoque normalizado
En un esquema completamente normalizado, cada dato se almacena en su propia fila. Si un usuario puede tener varios números de teléfono, debe crear una tabla user_phones con una clave foránea que apunte a users.
Este es el modelo relacional clásico y la opción predeterminada en la mayoría de las situaciones.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE user_phones (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
phone TEXT NOT NULL
);
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
INSERT INTO user_phones (user_id, phone) VALUES
(1, '+1-555-0101'),
(1, '+1-555-0102'),
(2, '+1-555-0200');El enfoque con arrays
El tipo TEXT[] de PostgreSQL (o cualquier otro tipo seguido de []) permite almacenar varios valores directamente dentro de una sola columna. No se necesita ninguna tabla adicional.
Los mismos datos de números de teléfono pueden almacenarse en una única fila compacta por usuario.
CREATE TABLE users_with_phones (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
phones TEXT[]
);
INSERT INTO users_with_phones (name, phones) VALUES
('Alice', ARRAY['+1-555-0101', '+1-555-0102']),
('Bob', ARRAY['+1-555-0200']);Consultar arrays es fácil
Buscar dentro de una columna de tipo array es sencillo con el operador ANY o el operador @> (contiene). Puede encontrar todos los usuarios que tienen un número de teléfono específico con una cláusula WHERE sencilla.
-- Find users who have a specific phone number
SELECT name
FROM users_with_phones
WHERE '+1-555-0101' = ANY(phones);
-- Or using the array-contains operator
SELECT name
FROM users_with_phones
WHERE phones @> ARRAY['+1-555-0101'];Cuándo convienen los arrays: consultas sencillas
Los arrays son una buena opción cuando:
- La lista de valores se lee en conjunto como una unidad (etiquetas, rótulos, categorías)
- Nunca necesita hacer un join con elementos individuales
- La lista tiene un límite superior natural y rara vez se actualiza por partes
Un ejemplo clásico es almacenar las etiquetas de una entrada de blog. Siempre obtiene todas las etiquetas a la vez y rara vez consulta las entradas por una sola etiqueta en un join complejo.
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
tags TEXT[]
);
INSERT INTO posts (title, tags) VALUES
('Intro to SQL', ARRAY['sql', 'beginner', 'database']),
('Advanced Indexes', ARRAY['sql', 'performance', 'indexes']),
('NoSQL Overview', ARRAY['nosql', 'beginner']);
-- Get all posts tagged 'beginner'
SELECT title FROM posts
WHERE 'beginner' = ANY(tags);Cuándo convienen las tablas normalizadas: relaciones
Las tablas normalizadas son la mejor opción cuando:
- Los valores individuales necesitan sus propios atributos (por ejemplo, un número de teléfono tiene un tipo: personal o del trabajo)
- Necesita hacer un join con valores individuales
- Los valores cambian de forma independiente y con frecuencia
- Necesita integridad referencial mediante claves foráneas
-- Phone numbers need a 'type' attribute — array can't do this cleanly
CREATE TABLE user_phones (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
phone TEXT NOT NULL,
type TEXT CHECK (type IN ('home', 'work', 'mobile'))
);
INSERT INTO user_phones (user_id, phone, type) VALUES
(1, '+1-555-0101', 'home'),
(1, '+1-555-0102', 'work');La diferencia en los índices
Con una tabla normalizada, puede añadir un índice B-tree estándar sobre la clave foránea o la columna de valores. Con los arrays, necesita un índice GIN (Generalized Inverted Index) para habilitar búsquedas rápidas dentro del array.
Los índices GIN funcionan bien, pero son más grandes y más lentos de actualizar que los índices B-tree.
-- Index for fast array element lookups
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);
-- Now this query uses the index efficiently
EXPLAIN SELECT title FROM posts
WHERE tags @> ARRAY['sql'];Agregar entre filas: la ventaja de la normalización
Cuando necesita contar, agrupar o agregar valores individuales, las tablas normalizadas resultan mucho más naturales. Agregar dentro de arrays requiere unnest(), que primero expande el array en filas y, en esencia, recrea la estructura normalizada en el momento de la consulta.
-- Count posts per tag (array approach — needs unnest)
SELECT tag, COUNT(*) AS post_count
FROM posts, unnest(tags) AS tag
GROUP BY tag
ORDER BY post_count DESC;
-- With a normalized post_tags table this would be simpler:
-- SELECT tag, COUNT(*) FROM post_tags GROUP BY tag;Modificar elementos de un array
Actualizar o eliminar un solo elemento dentro de un array requiere una sintaxis poco práctica: debe reemplazar el array completo o usar array_remove(). En una tabla normalizada, solo tiene que aplicar DELETE o UPDATE a la fila específica.
-- Remove a single tag from an array column
UPDATE posts
SET tags = array_remove(tags, 'beginner')
WHERE id = 1;
-- Append a new tag
UPDATE posts
SET tags = array_append(tags, 'tutorial')
WHERE id = 1;
SELECT title, tags FROM posts WHERE id = 1;Garantizar valores válidos
En una tabla normalizada, puede usar una clave foránea para garantizar que cada valor proceda de un conjunto conocido. Los arrays no pueden hacer referencia a otra tabla, ya que no admiten claves foráneas.
Si necesita garantizar la integridad referencial de cada elemento, una tabla hija es la única opción.
-- Normalized: only valid category IDs allowed (FK enforced)
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL
);
CREATE TABLE post_categories (
post_id INT REFERENCES posts(id),
category_id INT REFERENCES categories(id),
PRIMARY KEY (post_id, category_id)
);
-- Array: no constraint possible — any text value is accepted
-- UPDATE posts SET tags = ARRAY['totally_invalid_tag'] WHERE id = 1;Guía práctica para decidir
Utilice un array cuando los datos sean una lista plana y sencilla, siempre se lean como una unidad, no tengan atributos adicionales por elemento y no sea necesaria la integridad referencial (por ejemplo, etiquetas, rótulos o palabras clave de búsqueda).
Utilice una tabla hija normalizada cuando cada elemento tenga sus propios atributos, necesite hacer joins o agregaciones sobre valores individuales, necesite claves foráneas o actualice o elimine elementos individuales con frecuencia.
-- Summary example: tags as array (good fit)
SELECT title, tags
FROM posts
WHERE tags @> ARRAY['sql']
ORDER BY title;
-- Unnest when you need row-level processing
SELECT title, unnest(tags) AS tag
FROM posts
ORDER BY title, tag;Comprobación rápida
¿Qué escenario es la mejor opción para almacenar los datos como un array de PostgreSQL en lugar de hacerlo en una tabla hija normalizada?
Repaso de la lección
En esta lección aprendió cuáles son las principales ventajas y desventajas de los arrays y las tablas normalizadas en PostgreSQL.
- Los arrays son compactos y prácticos para listas planas que se leen como una unidad, como las etiquetas, pero no admiten claves foráneas, dificultan las actualizaciones por elemento y requieren índices GIN para realizar búsquedas rápidas.
- Las tablas normalizadas admiten atributos por elemento, integridad mediante claves foráneas, agregaciones eficientes y actualizaciones sencillas a nivel de fila, a cambio de necesitar un join adicional.
- La opción adecuada depende de cómo consulte, actualice y relacione los datos, no solo de cómo los almacene.
Preguntas frecuentes
¿La lección «Arrays frente a tablas normalizadas» es gratis?
Sí — el texto completo de «Arrays frente a tablas normalizadas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de SQL Academy, actualiza a CoddyKit PRO. El curso de SQL Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Arrays frente a tablas normalizadas»?
Cuándo los arrays son la opción adecuada Practicas SQL Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar SQL Academy?
No se requiere experiencia previa. SQL Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Arrays frente a tablas normalizadas»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de SQL Academy?
Sí. Cada lección de SQL Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Conceptos básicos de las columnas de arrays
- Búsqueda dentro de arrays
- UNNEST y agregación
- Arrays frente a tablas normalizadas