INSERT INTO SELECT
La sentencia INSERT INTO ... SELECT inserta filas en una tabla tomándolas del resultado de una consulta SELECT. En lugar de proporcionar valores literales, los datos vienen de otra tabla (o de la misma tabla con transformaciones). Es la herramienta estándar para copiar datos, crear respaldos parciales y generar tablas de resumen.
Sintaxis
INSERT INTO tabla_destino (columna1, columna2, ...)
SELECT expresion1, expresion2, ...
FROM tabla_origen
WHERE condicion;Las columnas del SELECT deben coincidir en número y tipo (o tipo compatible) con las columnas de destino. No se usa la palabra VALUES.
Copiar datos a una tabla de respaldo
Un patrón muy habitual: crear una copia de seguridad de una tabla antes de hacer cambios. Primero creamos la tabla de destino y después copiamos los datos:
CREATE TABLE clientes_backup (
id INT PRIMARY KEY,
nombre VARCHAR(100),
apellidos VARCHAR(100),
email VARCHAR(150),
ciudad VARCHAR(100)
);
INSERT INTO clientes_backup (id, nombre, apellidos, email, ciudad)
SELECT id, nombre, apellidos, email, ciudad
FROM clientes;Query OK, 20 rows affected (0.01 sec)
Records: 20 Duplicates: 0 Warnings: 0
Los 20 clientes se copian a la tabla de respaldo. Solo copiamos las columnas que nos interesan, no necesariamente todas.
Copiar con filtros
Puedes añadir un WHERE para copiar solo un subconjunto de datos. Crear una tabla con los clientes de Madrid:
CREATE TABLE clientes_madrid (
id INT PRIMARY KEY,
nombre VARCHAR(100),
apellidos VARCHAR(100),
email VARCHAR(150)
);
INSERT INTO clientes_madrid
SELECT id, nombre, apellidos, email
FROM clientes
WHERE ciudad = 'Madrid';Query OK, 2 rows affected (0.01 sec)
Solo María y Pedro (los dos clientes de Madrid) se copian a la nueva tabla.
Insertar datos agregados
INSERT INTO ... SELECT brilla cuando combinas datos de varias tablas. Crear una tabla de resumen de ventas por categoría:
CREATE TABLE resumen_ventas (
categoria_id INT PRIMARY KEY,
categoria VARCHAR(100),
productos_vendidos INT,
unidades INT,
ingresos DECIMAL(12, 2)
);
INSERT INTO resumen_ventas
SELECT
c.id,
c.nombre,
COUNT(DISTINCT dp.producto_id),
SUM(dp.cantidad),
SUM(dp.cantidad * dp.precio_unitario)
FROM categorias c
JOIN productos p ON c.id = p.categoria_id
JOIN detalle_pedidos dp ON p.id = dp.producto_id
GROUP BY c.id, c.nombre;Query OK, 11 rows affected (0.01 sec)
La tabla resumen_ventas contiene ahora un resumen precalculado con los datos de 3 tablas combinadas. Este tipo de tablas de resumen son muy útiles en dashboards donde las consultas de agregación serían demasiado lentas en tiempo real.
INSERT INTO SELECT desde la misma tabla
Puedes insertar datos en una tabla leyendo de la misma tabla. Copiar las etiquetas de un producto a otro:
INSERT INTO etiquetas_producto (producto_id, etiqueta)
SELECT 3, etiqueta
FROM etiquetas_producto
WHERE producto_id = 1;Esto copia todas las etiquetas del producto 1 (iPhone 15 Pro: premium, más vendido, novedad) al producto 3 (Google Pixel 8). El SELECT sustituye el producto_id original por el valor literal 3.
INSERT INTO SELECT con JOINs
El SELECT puede incluir JOINs de cualquier complejidad. Crear una tabla con el historial de compras entregadas:
CREATE TABLE historial_compras (
cliente VARCHAR(200),
producto VARCHAR(200),
cantidad INT,
precio DECIMAL(10, 2),
fecha DATE
);
INSERT INTO historial_compras
SELECT
CONCAT(cl.nombre, ' ', cl.apellidos),
pr.nombre,
dp.cantidad,
dp.precio_unitario,
DATE(p.fecha_pedido)
FROM clientes cl
JOIN pedidos p ON cl.id = p.cliente_id
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
JOIN productos pr ON dp.producto_id = pr.id
WHERE p.estado = 'entregado';Query OK, 14 rows affected (0.01 sec)
Solo se copian los pedidos entregados. El JOIN de 4 tablas combina la información de clientes, pedidos, detalles y productos. Las expresiones como CONCAT() y DATE() transforman los datos durante la inserción.
CREATE TABLE ... AS SELECT
Una alternativa más concisa cuando quieres crear la tabla y llenarla en un solo paso:
CREATE TABLE productos_caros AS
SELECT id, nombre, precio, stock
FROM productos
WHERE precio > 500;MySQL crea la tabla automáticamente con las columnas y tipos que devuelve el SELECT. Es más rápido que crear la tabla manualmente y después hacer el INSERT, pero tienes menos control sobre los tipos de datos, las restricciones y los índices. Los índices y claves primarias no se copian.
Para añadir un índice después:
ALTER TABLE productos_caros ADD PRIMARY KEY (id);Limpiar tablas de ejemplo
Las tablas que hemos creado son tablas reales que ocupan espacio. Cuando ya no las necesites:
DROP TABLE IF EXISTS clientes_backup, clientes_madrid,
resumen_ventas, historial_compras, productos_caros;En el siguiente artículo veremos INSERT ... ON DUPLICATE KEY UPDATE, que permite actualizar una fila existente cuando el INSERT encuentra un duplicado en una clave única.
Escrito por Eduardo Lázaro
