¡Bienvenido al corazón de los escenarios dramáticos de carga masiva de datos! Hoy vamos a aprender a manejar de forma eficiente los errores que aparecen al cargar datos usando la construcción ON CONFLICT. Es como poner el piloto automático en un avión: aunque algo salga mal, sabrás qué hacer para evitar una catástrofe. ¡Vamos a descubrir los trucos de PostgreSQL!
¡A nadie le gustan las sorpresas, sobre todo cuando los datos se niegan a cargarse! En los procesos de carga masiva puedes encontrarte con varios problemas típicos:
- Duplicación de datos. Por ejemplo, si la tabla tiene una restricción
UNIQUEy tu archivo de datos está lleno de repeticiones. - Conflictos con restricciones. Por ejemplo, intenta cargar un valor vacío en una columna con restricción
NOT NULL. ¿El resultado? Error. PostgreSQL siempre es muy estricto en estas situaciones. - Información primaria duplicada. La tabla puede que ya tenga datos con los mismos identificadores que tu archivo CSV.
Vamos a ver cómo evitar estos "icebergs" usando ON CONFLICT.
Uso de ON CONFLICT para manejar errores
La sintaxis de ON CONFLICT te permite indicar qué hacer si hay un conflicto con las restricciones (por ejemplo, UNIQUE o PRIMARY KEY). PostgreSQL te da la opción de actualizar los datos existentes o simplemente ignorar la fila en conflicto.
Así se ve la sintaxis básica de ON CONFLICT:
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON CONFLICT (conflict_target)
DO UPDATE SET column1 = new_value1, column2 = new_value2;
Puedes cambiar DO UPDATE por DO NOTHING si solo quieres ignorar el conflicto.
Ejemplo: actualizar datos en caso de conflicto
Supón que tienes una tabla students:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT
);
Ahora queremos cargar nuevos datos, pero algunos ya existen en la base:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22), -- Este estudiante ya existe
(2, 'Anna', 20), -- Nuevo estudiante
(3, 'Mal', 25) -- Nuevo estudiante
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age;
En este ejemplo, si ya existe un estudiante con el ID que queremos añadir, sus datos serán actualizados:
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age;
Fíjate en la palabra mágica EXCLUDED. Significa "los valores que intentaste insertar pero fueron excluidos por el conflicto".
Resultado:
- El estudiante con
id = 1actualizará sus datos (nombre y edad). - Los estudiantes con
id = 2yid = 3serán añadidos a la tabla.
Ejemplo: ignorar conflictos
Si no quieres actualizar datos, sino solo ignorar las filas que causan conflicto, usa DO NOTHING:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22), -- Este estudiante ya existe
(2, 'Anna', 20), -- Nuevo estudiante
(3, 'Mal', 25) -- Nuevo estudiante
ON CONFLICT (id) DO NOTHING;
Ahora las filas en conflicto simplemente no se insertarán, y las demás entrarán tranquilamente en tu base.
Registro de errores
A veces ignorar o actualizar no es suficiente. Por ejemplo, necesitas registrar los conflictos para analizarlos después. Podemos crear una tabla especial para registrar errores:
CREATE TABLE conflict_log (
conflict_time TIMESTAMP DEFAULT NOW(),
id INT,
name TEXT,
age INT,
conflict_reason TEXT
);
Luego añadimos el manejo de errores con registro:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22),
(2, 'Anna', 20),
(3, 'Mal', 25)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age
RETURNING EXCLUDED.id, EXCLUDED.name, EXCLUDED.age
INTO conflict_log;
Este último ejemplo solo funcionará dentro de procedimientos almacenados. Cómo funciona exactamente lo verás cuando estudiemos PL-SQL. Me adelanté un poco, solo quería mostrarte otra forma de resolver conflictos al cargar datos: registrar todas las filas problemáticas.
Ahora puedes analizar las razones de los conflictos. Esta técnica es especialmente útil en sistemas complejos donde es importante guardar "rastros" durante cargas masivas de datos.
Ejemplo práctico
Vamos a juntar todo lo aprendido en una tarea sencilla. Imagina que tienes un archivo CSV con actualizaciones de estudiantes que quieres cargar en la tabla:
Archivo students_update.csv
| id | name | age |
|---|---|---|
| 1 | Otto | 23 |
| 2 | Anna | 21 |
| 4 | Wally | 30 |
Carga de datos y manejo de conflictos
- Primero creamos una tabla temporal
tmp_students:
CREATE TEMP TABLE tmp_students (
id INTEGER,
name TEXT,
age INTEGER
);
- Cargamos los datos del archivo usando
\COPY:
\COPY tmp_students FROM 'students_update.csv' DELIMITER ',' CSV HEADER
- Insertamos los datos de la tabla temporal en la permanente usando
INSERT ON CONFLICT:
INSERT INTO students (id, name, age)
SELECT id, name, age FROM tmp_students
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
age = EXCLUDED.age;
Ahora todos los datos, incluidas las actualizaciones (la fila con id = 1), se han cargado correctamente.
Errores típicos y cómo evitarlos
Los errores pasan incluso a los programadores más experimentados, pero si sabes cómo evitarlos, te ahorrarás horas (¡o incluso días!) de nervios.
- Conflicto con la restricción
UNIQUE. Asegúrate de indicar el campo correcto enON CONFLICT. Por ejemplo, si pones la clave equivocada (iden vez deemail), PostgreSQL simplemente le dirá "adiós" a tu consulta. - Uso incorrecto de
EXCLUDED. Este alias solo se refiere a los valores pasados en la consulta actual. No intentes usarlo en otros contextos. - Omisión de columnas. Asegúrate de que todas las columnas indicadas en
SETexisten en la tabla. Por ejemplo, si añadesSET non_existing_column = 'value', tendrás un error.
Usar ON CONFLICT hace que la carga masiva de datos en PostgreSQL sea flexible y segura. No solo puedes evitar que tus consultas fallen por conflictos, sino que también controlas cómo se manejan tus datos. Tus usuarios (¡y tus servidores!) te lo agradecerán.
GO TO FULL VERSION