MERGE
El motor MERGE, también conocido como MRG_MyISAM, permite combinar varias tablas MyISAM con estructura idéntica en una sola tabla lógica. Cuando consultas una tabla MERGE, MySQL busca automáticamente en todas las tablas subyacentes como si fueran una sola. Esta funcionalidad fue diseñada originalmente como una forma de particionar datos antes de que MySQL incorporara el particionamiento nativo.
En la práctica moderna, el motor MERGE es un remanente histórico que tiene muy pocos casos de uso válidos. Con la disponibilidad del particionamiento nativo de InnoDB y las capacidades avanzadas de este motor, MERGE se utiliza casi exclusivamente en sistemas heredados que aún dependen de tablas MyISAM.
Sintaxis
Para crear una tabla MERGE, primero necesitas las tablas MyISAM subyacentes con estructura idéntica, y luego defines la tabla MERGE que las agrupa:
CREATE TABLE nombre_merge (
-- mismas columnas que las tablas subyacentes
) ENGINE = MERGE
UNION = (tabla1, tabla2, tabla3)
INSERT_METHOD = LAST;La cláusula UNION especifica las tablas MyISAM que se combinan, y INSERT_METHOD define dónde se insertan nuevas filas.
Comportamiento básico
Para entender cómo funciona MERGE, partamos de un escenario donde tienes registros de ventas divididos por trimestre en tablas separadas. Cada tabla tiene exactamente la misma estructura:
CREATE TABLE ventas_t1 (
id INT AUTO_INCREMENT PRIMARY KEY,
fecha DATE NOT NULL,
producto VARCHAR(100) NOT NULL,
monto DECIMAL(10,2) NOT NULL
) ENGINE = MyISAM;
CREATE TABLE ventas_t2 (
id INT AUTO_INCREMENT PRIMARY KEY,
fecha DATE NOT NULL,
producto VARCHAR(100) NOT NULL,
monto DECIMAL(10,2) NOT NULL
) ENGINE = MyISAM;
CREATE TABLE ventas_t3 (
id INT AUTO_INCREMENT PRIMARY KEY,
fecha DATE NOT NULL,
producto VARCHAR(100) NOT NULL,
monto DECIMAL(10,2) NOT NULL
) ENGINE = MyISAM;Insertamos datos en cada tabla trimestral:
INSERT INTO ventas_t1 VALUES (1, '2024-02-15', 'Laptop', 1200.00);
INSERT INTO ventas_t1 VALUES (2, '2024-03-20', 'Monitor', 450.00);
INSERT INTO ventas_t2 VALUES (1, '2024-05-10', 'Teclado', 85.00);
INSERT INTO ventas_t2 VALUES (2, '2024-06-22', 'Laptop', 1350.00);
INSERT INTO ventas_t3 VALUES (1, '2024-08-05', 'Auriculares', 120.00);Ahora creamos la tabla MERGE que las combina:
CREATE TABLE ventas_anual (
id INT AUTO_INCREMENT PRIMARY KEY,
fecha DATE NOT NULL,
producto VARCHAR(100) NOT NULL,
monto DECIMAL(10,2) NOT NULL
) ENGINE = MERGE
UNION = (ventas_t1, ventas_t2, ventas_t3)
INSERT_METHOD = LAST;Al consultar ventas_anual, obtienes los datos de las tres tablas combinados:
SELECT * FROM ventas_anual ORDER BY fecha;| id | fecha | producto | monto |
|---|---|---|---|
| 1 | 2024-02-15 | Laptop | 1200.00 |
| 2 | 2024-03-20 | Monitor | 450.00 |
| 1 | 2024-05-10 | Teclado | 85.00 |
| 2 | 2024-06-22 | Laptop | 1350.00 |
| 1 | 2024-08-05 | Auriculares | 120.00 |
Observa que los valores de id se repiten porque cada tabla subyacente tiene su propio auto_increment independiente.
Caso práctico: INSERT_METHOD
La cláusula INSERT_METHOD controla en cuál de las tablas subyacentes se insertan los nuevos registros cuando haces un INSERT en la tabla MERGE. Tiene tres valores posibles:
-- LAST: inserta en la última tabla de la lista UNION
CREATE TABLE merge_ejemplo (...) ENGINE = MERGE
UNION = (tabla_a, tabla_b, tabla_c) INSERT_METHOD = LAST;
-- FIRST: inserta en la primera tabla de la lista UNION
CREATE TABLE merge_ejemplo (...) ENGINE = MERGE
UNION = (tabla_a, tabla_b, tabla_c) INSERT_METHOD = FIRST;
-- NO: no permite inserciones (solo lectura)
CREATE TABLE merge_ejemplo (...) ENGINE = MERGE
UNION = (tabla_a, tabla_b, tabla_c) INSERT_METHOD = NO;Con INSERT_METHOD = LAST, los nuevos registros van a la tabla ventas_t3:
INSERT INTO ventas_anual (fecha, producto, monto)
VALUES ('2024-09-10', 'Webcam HD', 65.00);
-- Verificar que se insertó en ventas_t3
SELECT * FROM ventas_t3;| id | fecha | producto | monto |
|---|---|---|---|
| 1 | 2024-08-05 | Auriculares | 120.00 |
| 2 | 2024-09-10 | Webcam HD | 65.00 |
El registro aparece en ventas_t3 porque es la última tabla de la lista UNION.
Caso práctico: datos históricos particionados por periodo
El caso de uso histórico más común para MERGE era gestionar datos que crecen con el tiempo, donde los datos antiguos se consultan con menos frecuencia. Al dividirlos en tablas separadas, puedes archivar o eliminar periodos antiguos sin afectar los datos recientes.
-- Tablas por año
CREATE TABLE logs_2022 (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL
) ENGINE = MyISAM;
CREATE TABLE logs_2023 (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL
) ENGINE = MyISAM;
CREATE TABLE logs_2024 (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL
) ENGINE = MyISAM;
-- Tabla MERGE que las agrupa
CREATE TABLE logs_todos (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL
) ENGINE = MERGE
UNION = (logs_2022, logs_2023, logs_2024)
INSERT_METHOD = LAST;Para archivar un año antiguo, simplemente lo eliminas de la lista UNION sin perder los datos:
-- Recrear la tabla MERGE sin el año 2022
ALTER TABLE logs_todos
UNION = (logs_2023, logs_2024);Los datos de logs_2022 siguen existiendo en su tabla y pueden consultarse directamente, pero ya no aparecen en logs_todos.
Para añadir un nuevo año:
CREATE TABLE logs_2025 (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL
) ENGINE = MyISAM;
ALTER TABLE logs_todos
UNION = (logs_2023, logs_2024, logs_2025)
INSERT_METHOD = LAST;Limitaciones y alternativas modernas
El motor MERGE tiene restricciones significativas. Solo funciona con tablas MyISAM subyacentes, lo que significa que hereda todas las limitaciones de MyISAM: sin transacciones, sin claves foráneas y con bloqueo a nivel de tabla. Las tablas subyacentes deben tener exactamente la misma estructura, incluyendo tipos de columna y orden.
Además, los DELETE y UPDATE en tablas MERGE pueden comportarse de forma inesperada cuando afectan a registros en diferentes tablas subyacentes.
En el MySQL moderno, la alternativa recomendada es el particionamiento nativo de InnoDB:
-- Alternativa moderna: particionamiento InnoDB
CREATE TABLE logs (
id INT AUTO_INCREMENT,
timestamp DATETIME NOT NULL,
nivel VARCHAR(10) NOT NULL,
mensaje VARCHAR(500) NOT NULL,
PRIMARY KEY (id, timestamp)
) ENGINE = InnoDB
PARTITION BY RANGE (YEAR(timestamp)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);Esta solución ofrece la misma capacidad de particionar datos por periodo, pero con todas las ventajas de InnoDB: transacciones, bloqueo a nivel de fila y recuperación automática.
Advertencia: No crees nuevas tablas MERGE en proyectos nuevos. Esta funcionalidad está estrechamente ligada a MyISAM y no tiene evolución futura en MySQL. Usa el particionamiento nativo de InnoDB si necesitas dividir datos en segmentos lógicos.
Consejo: Si mantienes un sistema heredado con tablas MERGE, planifica la migración a InnoDB particionado. El proceso implica crear la nueva tabla particionada, copiar los datos con
INSERT INTO ... SELECTy luego renombrar las tablas para completar la transición.
En el siguiente artículo aprenderás sobre el motor ARCHIVE, diseñado para almacenar grandes volúmenes de datos comprimidos de forma eficiente.
Escrito por Eduardo Lázaro
