El proveedor manda los precios de octubre en un CSV. Para importar un CSV lo más seguro es cargarlo primero en una tabla aparte (de «llegada»), revisarlo, y después actualizar la tabla de verdad con SQL. Cada motor tiene su herramienta de carga, con sus trampas.
También te puede interesar
El archivo
Con cabecera, un nombre entre comillas porque lleva una coma y otro con comillas dobles escapadas (""), como manda el formato CSV:
id,nombre,precio,stock 3,"Monitor 27"" 4K",399.90,12 6,Ratón inalámbrico,22.99,60 8,SSD 1 TB,89.90,45 16,"Hub USB-C, 7 puertos",34.90,12
SQLite: .import en la consola sqlite3
.import no es SQL: es una orden de la consola sqlite3 (las que empiezan por punto). Con --csv entiende las comillas y con --skip 1 se salta la cabecera. Después, un UPDATE … FROM (SQLite 3.33+) actualiza los que existen y un INSERT … SELECT añade los nuevos:
-- Script para la consola sqlite3: las líneas que empiezan por punto son órdenes de la consola, no SQL .read /datos/tienda.sql CREATE TABLE llegada (id INTEGER, nombre TEXT, precio REAL, stock INTEGER); .import --csv --skip 1 precios-octubre.csv llegada .mode box SELECT * FROM llegada; -- Actualizar precios y stock de los que ya existen UPDATE productos SET precio = l.precio, stock = l.stock FROM llegada l WHERE productos.id = l.id; -- Dar de alta los nuevos INSERT INTO productos (id, nombre, categoria_id, precio, stock) SELECT id, nombre, NULL, precio, stock FROM llegada WHERE id NOT IN (SELECT id FROM productos); SELECT id, nombre, precio, stock FROM productos WHERE id IN (3, 6, 8, 16);
sqlite3 :memory: < importar.sqlite3
salida · sqlite3 3.46.1┌────┬──────────────────────┬────────┬───────┐ │ id │ nombre │ precio │ stock │ ├────┼──────────────────────┼────────┼───────┤ │ 3 │ Monitor 27" 4K │ 399.9 │ 12 │ │ 6 │ Ratón inalámbrico │ 22.99 │ 60 │ │ 8 │ SSD 1 TB │ 89.9 │ 45 │ │ 16 │ Hub USB-C, 7 puertos │ 34.9 │ 12 │ └────┴──────────────────────┴────────┴───────┘ ┌────┬──────────────────────┬────────┬───────┐ │ id │ nombre │ precio │ stock │ ├────┼──────────────────────┼────────┼───────┤ │ 3 │ Monitor 27" 4K │ 399.9 │ 12 │ │ 6 │ Ratón inalámbrico │ 22.99 │ 60 │ │ 8 │ SSD 1 TB │ 89.9 │ 45 │ │ 16 │ Hub USB-C, 7 puertos │ 34.9 │ 12 │ └────┴──────────────────────┴────────┴───────┘
Si la tabla no existe, .import la crea usando la primera línea como nombres de columna, pero todas las columnas son de texto. Y como texto, '89.90' va después de '399.90':
-- Sin crear la tabla antes: .import la crea con la cabecera como nombres y todo como TEXT
.import --csv precios-octubre.csv llegada
.mode box
SELECT name, type FROM pragma_table_info('llegada');
SELECT nombre, precio, typeof(precio) AS tipo FROM llegada WHERE id = '3';
-- Como texto, '89.90' va antes que '399.90' al ordenar de mayor a menor
SELECT nombre, precio FROM llegada ORDER BY precio DESC;salida · sqlite3┌────────┬──────┐ │ name │ type │ ├────────┼──────┤ │ id │ TEXT │ │ nombre │ TEXT │ │ precio │ TEXT │ │ stock │ TEXT │ └────────┴──────┘ ┌────────────────┬────────┬──────┐ │ nombre │ precio │ tipo │ ├────────────────┼────────┼──────┤ │ Monitor 27" 4K │ 399.90 │ text │ └────────────────┴────────┴──────┘ ┌──────────────────────┬────────┐ │ nombre │ precio │ ├──────────────────────┼────────┤ │ SSD 1 TB │ 89.90 │ │ Monitor 27" 4K │ 399.90 │ │ Hub USB-C, 7 puertos │ 34.90 │ │ Ratón inalámbrico │ 22.99 │ └──────────────────────┴────────┘
PostgreSQL: COPY
COPY … FROM lee un archivo del servidor; necesita permisos de superusuario o del rol pg_read_server_files. Desde tu equipo, la orden de la consola psql \copy hace lo mismo con un archivo local, con la misma sintaxis.
CREATE TABLE llegada (id INTEGER, nombre VARCHAR(60), precio NUMERIC(10,2), stock INTEGER); -- COPY lee el archivo en el servidor; con psql, \copy hace lo mismo con un archivo de tu equipo COPY llegada FROM '/w/precios-octubre.csv' WITH (FORMAT csv, HEADER true); SELECT * FROM llegada; UPDATE productos p SET precio = l.precio, stock = l.stock FROM llegada l WHERE p.id = l.id; INSERT INTO productos (id, nombre, categoria_id, precio, stock) SELECT id, nombre, NULL, precio, stock FROM llegada WHERE id NOT IN (SELECT id FROM productos); SELECT id, nombre, precio, stock FROM productos WHERE id IN (3, 6, 8, 16) ORDER BY id;
resultado · PostgreSQL id | nombre | precio | stock ----+----------------------+--------+------- 3 | Monitor 27" 4K | 399.90 | 12 6 | Ratón inalámbrico | 22.99 | 60 8 | SSD 1 TB | 89.90 | 45 16 | Hub USB-C, 7 puertos | 34.90 | 12 id | nombre | precio | stock ----+----------------------+--------+------- 3 | Monitor 27" 4K | 399.90 | 12 6 | Ratón inalámbrico | 22.99 | 60 8 | SSD 1 TB | 89.90 | 45 16 | Hub USB-C, 7 puertos | 34.90 | 12
El primer resultado es la tabla de llegada; el segundo, los productos ya actualizados (el hub USB-C es nuevo).
MySQL: LOAD DATA LOCAL INFILE
La forma de MySQL es LOAD DATA. Con LOCAL lee un archivo del equipo del cliente, pero viene desactivado por seguridad, en el servidor y en el cliente:
CREATE TABLE llegada (id INTEGER, nombre VARCHAR(60), precio DECIMAL(10,2), stock INTEGER); LOAD DATA LOCAL INFILE '/w/precios-octubre.csv' INTO TABLE llegada FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' IGNORE 1 LINES;
resultado · MySQLERROR 3948 (42000) at line 3: Loading local data is disabled; this must be enabled on both the client and server sides
Hay que activarlo en los dos lados: en el servidor, local_infile, y en el cliente, arrancando mysql con --local-infile=1:
mysql --local-infile=1 -u usuario -p tienda < cargar.sql
-- mysql: --local-infile=1 -- Con el cliente arrancado con --local-infile=1 y el servidor con local_infile activado SET GLOBAL local_infile = 1; CREATE TABLE llegada (id INTEGER, nombre VARCHAR(60), precio DECIMAL(10,2), stock INTEGER); LOAD DATA LOCAL INFILE '/w/precios-octubre.csv' INTO TABLE llegada FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' IGNORE 1 LINES; SELECT * FROM llegada;
resultado · MySQL+------+----------------------+--------+-------+ | id | nombre | precio | stock | +------+----------------------+--------+-------+ | 3 | Monitor 27" 4K | 399.90 | 12 | | 6 | Ratón inalámbrico | 22.99 | 60 | | 8 | SSD 1 TB | 89.90 | 45 | | 16 | Hub USB-C, 7 puertos | 34.90 | 12 | +------+----------------------+--------+-------+
| Motor | Orden | Lee el archivo de… |
|---|---|---|
| SQLite | .import --csv (consola sqlite3) | Tu equipo |
| PostgreSQL | COPY … FROM | El servidor |
| PostgreSQL | \copy … FROM (consola psql) | Tu equipo |
| MySQL | LOAD DATA INFILE | El servidor (carpeta secure_file_priv) |
| MySQL | LOAD DATA LOCAL INFILE | Tu equipo, si está activado en los dos lados |
Todas las consultas de esta página se ejecutaron antes de publicarla sobre la base de ejemplo «tienda» del curso, en contenedores limpios: SQLite 3.46.1 (el de Python 3.13), PostgreSQL 17 y MySQL 8.4. Los resultados y los mensajes de error están copiados de esa ejecución. Documentación oficial: COPY en PostgreSQL 17.