Tema SQL
INSERT … ON DUPLICATE KEY UPDATE (MySQL / MariaDB)
Inserta una fila o, si hay conflicto con clave primaria o única, ejecuta UPDATE en su lugar. Patrón upsert muy usado; en PostgreSQL suele usarse ON CONFLICT; en SQL Server MERGE.
Descripción
ON DUPLICATE KEY UPDATE col = VALUES(col) permite «guardar o actualizar» en un solo viaje. VALUES() referencia los valores propuestos en el INSERT (en MySQL 8.0.20+ se recomienda alias en INSERT AS new para evitar deprecación). Cuidado con condiciones de carrera y triggers. No es estándar SQL puro. Para claves compuestas, cualquier índice único violado dispara la rama UPDATE. Replica el comportamiento en migraciones probando con concurrencia.
Estructura general
Forma habitual de la sentencia: orden de cláusulas, qué parte filtra, agrupa o proyecta columnas, y qué devuelve el motor. Cópiala como plantilla y sustituye nombres por tus tablas reales.
INSERT INTO tabla (a, b) VALUES (?, ?)
ON DUPLICATE KEY UPDATE b = VALUES(b), c = c + 1;
Componentes
Cada componente tiene un papel concreto en la ejecución lógica de la sentencia. Léelos en orden: así entiendes qué puedes omitir, qué es obligatorio y qué expresiones son válidas en cada sitio según tu motor.
- Clave única
- El conflicto debe ser detectable por índice PRIMARY o UNIQUE.
- PostgreSQL
- INSERT … ON CONFLICT (cols) DO UPDATE.
- Efectos
- Triggers BEFORE INSERT y UPDATE pueden interactuar de forma sutil.
Ejemplos
Ejemplo 1
INSERT INTO visitas (usuario_id, contador)
VALUES (7, 1)
ON DUPLICATE KEY UPDATE contador = contador + 1;
INSERT INTO sync_claves (k, v) VALUES ('x', '1')
ON DUPLICATE KEY UPDATE v = VALUES(v);
Ejemplo 2
INSERT INTO contadores (clave, n) VALUES ('visitas', 1)
ON DUPLICATE KEY UPDATE n = n + 1, actualizado = CURRENT_TIMESTAMP;
Los ejemplos son ilustrativos. En producción usa consultas parametrizadas (prepared statements), revisa índices y aplica el principio de menor privilegio en cuentas de base de datos.