Guías

Cómo convertir un Excel o un CSV a INSERT SQL sin romper la importación

Generar los INSERT es lo fácil. Lo que rompe una importación es el punto y coma del Excel en español, el O'Brien sin escapar, la fecha que se da la vuelta o el código postal que pierde el cero. Aquí están los fallos reales, motor por motor, y cómo evitarlos.

La herramienta de esta guía

CSV a SQL y otros formatos

Funciona dentro de tu navegador: nada de lo que pongas sale de esta página.

Convertir mi CSV o Excel a SQL

¿INSERT generados o el cargador masivo del motor?

Cada motor tiene un cargador nativo que lee el CSV directamente y es mucho más rápido que ejecutar miles de sentencias:

  • MySQL / MariaDB: LOAD DATA LOCAL INFILE 'clientes.csv' INTO TABLE clientes FIELDS TERMINATED BY ';' IGNORE 1 LINES;
  • PostgreSQL: desde psql, \copy clientes FROM 'clientes.csv' WITH (FORMAT csv, HEADER true, DELIMITER ';')
  • SQL Server: BULK INSERT clientes FROM 'C:\datos\clientes.csv' WITH (FORMAT = 'CSV', FIRSTROW = 2, FIELDTERMINATOR = ';');
  • SQLite: en la consola, .import --csv clientes.csv clientes

Si vas a cargar millones de filas, o la carga se repite cada noche, usa el cargador. Pero todos tienen letra pequeña. LOAD DATA LOCAL exige local_infile activado en servidor y cliente, y MySQL 8 lo trae desactivado; en un hosting compartido casi nunca puedes cambiarlo. COPY ... FROM 'ruta' lee el archivo en la máquina del servidor y pide superusuario o el rol pg_read_server_files. BULK INSERT busca la ruta en el disco del servidor, no en tu portátil, y en Azure SQL Database solo lee de Blob Storage.

Los INSERT generados ganan cuando solo tienes un cliente SQL o phpMyAdmin contra una base gestionada, cuando quieres revisar los datos antes de que entren o guardarlos como script de semilla en el repositorio, cuando el volumen es moderado (hasta unas decenas de miles de filas) o cuando otra persona ejecutará el SQL en un entorno al que tú no llegas.

Los fallos que rompen una importación

SíntomaCausaSolución
Todo cae en una sola columnaEl Excel en español guardó el CSV con ;Detectar el separador real
Error de sintaxis cerca de un apellidoComilla sin escapar en O'BrienDoblarla: 'O''Brien'
Vacíos que deberían ser NULL, o al revésEl CSV no distingue vacío de NULLUna regla fija por columna
12,50 desplaza las columnasComa decimal sin convertirEmitir el literal con punto
El 3 de abril aparece como 4 de marzodd/mm leído como mm/ddNormalizar a aaaa-mm-dd
Fechas que salen como 45000Número de serie de ExcelSumar los días a 1899-12-30
El código postal 08001 queda en 8001Columna tratada como númeroTipo texto en origen y destino
ñ o � en lugar de eñesWindows-1252 y UTF-8 mezcladosGuardar como «CSV UTF-8»
Error 10738 en SQL ServerMás de 1000 filas en un VALUESLotes de 1000 o menos
Carga a medias tras un errorSin transacciónBEGIN / COMMIT

Coma o punto y coma: por qué tu CSV de Excel no se separa bien

Al guardar como «CSV (delimitado por comas)», Excel usa el separador de listas de la configuración regional. En los países con coma decimal (España, Argentina, Colombia, Chile) ese separador es el punto y coma; en México, que usa punto decimal, es la coma. Un importador que espera comas y recibe ; te devuelve una sola columna gigante o un error de número de columnas.

Lo robusto es detectarlo. El conversor de CSV a SQL de Utiliobox prueba coma, tabulador, punto y coma y barra vertical en las cinco primeras líneas, sin contar los que van dentro de un campo entrecomillado. Si arrastras el .xlsx, el problema desaparece: se lee la primera hoja sin pasar por ningún CSV.

Comillas simples: el O'Brien que rompe el script

Una comilla simple dentro de un literal se escribe doblada: INSERT INTO clientes (apellido) VALUES ('O''Brien');. Vale en los cinco motores habituales. Concatenar el valor tal cual no solo da un error de sintaxis: es el mecanismo exacto de una inyección SQL. Dos matices por motor:

  • MySQL interpreta la barra invertida dentro de los literales: 'C:\nuevo' se guarda con un salto de línea en medio. Hay que doblarla. Ojo: con el modo NO_BACKSLASH_ESCAPES activo, doblarla guarda dos barras.
  • SQL Server necesita el prefijo N (N'Muñoz'). Sin él, lo que no existe en la página de códigos de la base se convierte en ?, aunque la columna sea NVARCHAR.

El conversor dobla la comilla en los cinco motores, la barra invertida en MySQL y añade la N en SQL Server.

NULL o cadena vacía

En un CSV, «no hay dato» y «texto vacío» son lo mismo: ;;. Cada cargador lo resuelve a su manera. COPY en formato CSV toma el campo vacío sin comillas como NULL y el "" como cadena vacía. LOAD DATA lo carga como cadena vacía (o 0 en columnas numéricas, según el modo SQL) y solo reconoce \N como NULL. Oracle guarda '' como NULL siempre.

El conversor aplica una regla clara: celda vacía, NULL. Y la usa en el CREATE TABLE: columna con algún hueco, NULL; sin ninguno, NOT NULL. Si tu CSV viene de un export que escribe la palabra NULL o \N, bórrala antes: se tomaría como texto y convertiría una columna numérica en texto.

Coma decimal y separador de miles

SQL solo acepta el punto decimal. Un 12,50 sin comillas dentro de un VALUES son dos valores, 12 y 50, y todas las columnas siguientes se desplazan. El conversor acepta coma o punto como separador decimal y siempre emite punto: 12,50 sale como 12.50 en un DECIMAL(18,2).

Lo que no interpreta son los separadores de miles. 1.234,56 no encaja como número y la columna queda como texto. El caso traicionero es 1,234: mil doscientos treinta y cuatro en un CSV estadounidense, uno coma dos tres cuatro en uno hispano. Si todos los números de la columna tienen esa forma, el conversor no apuesta y la deja como texto; basta un valor que lo aclare (12,5, 0,125) para que la lea como decimal. Lo más seguro sigue siendo quitar el separador de miles en Excel antes de exportar.

Fechas: dd/mm contra mm/dd y los números de serie de Excel

El único formato que todos los motores leen igual, sin depender de la configuración, es aaaa-mm-dd (con hh:mm:ss si hay hora). 03/04/2024 es el 3 de abril aquí y el 4 de marzo en Estados Unidos, y una importación que se equivoca no falla: carga mal en silencio.

El conversor reconoce fechas ISO y fechas con barra o guion y año de cuatro cifras, y decide el orden mirando la columna entera: si aparece un 25/12/2024, es día/mes; si aparece un 12/25/2024, mes/día. Si ningún valor pasa de 12 los datos no lo dicen, y en la página en español se supone día/mes; los tipos detectados lo marcan como «supuesto» para que lo compruebes. Si tu archivo viene de un sistema estadounidense y todas las fechas son ambiguas, normalízalo a aaaa-mm-dd antes. Una fecha imposible en los dos órdenes deja la columna como texto en vez de inventarse un mes. En Oracle las fechas salen como TO_DATE('2024-04-03', 'YYYY-MM-DD'), para no depender de NLS_DATE_FORMAT.

Si una celda pierde su formato verás el número de serie: 45000 es el 15 de marzo de 2023. Para convertirlo en el motor, suma los días al 30 de diciembre de 1899 (esa época ya absorbe el falso bisiesto de 1900 de Excel): DATE_ADD('1899-12-30', INTERVAL 45000 DAY) en MySQL, DATE '1899-12-30' + 45000 en PostgreSQL o DATEADD(day, 45000, '1899-12-30') en SQL Server.

Al leer un .xlsx, el conversor toma el texto que muestra cada celda, no el número interno, así que no verás seriales. Pero el formato corto de fecha puede llegar con año de dos cifras y entonces no se reconoce. Mira los tipos detectados bajo el panel: si una columna de fechas dice «texto», dale formato aaaa-mm-dd en Excel y vuelve a soltar el archivo.

Ceros a la izquierda: códigos postales, teléfonos y referencias

El código postal de Barcelona es 08001, no 8001. Son identificadores, no cantidades, y su tipo correcto es texto. Los ceros se pierden en dos sitios:

  • Excel los convierte en número al abrir el CSV, y si guardas ya no hay vuelta atrás. Importa con «Datos > Desde el texto/CSV» marcando la columna como texto.
  • Muchos conversores que infieren tipos proponen un entero para una columna de solo dígitos, y el valor sale como 8001. El de Utiliobox deja como texto cualquier columna donde algún valor empiece por cero, así que 08001 llega intacto. Si los ceros ya se perdieron antes, cambia esa columna a VARCHAR(5) en el CREATE TABLE y, tras cargar, recupéralos con UPDATE clientes SET cp = LPAD(cp, 5, '0'); (MySQL, PostgreSQL) o RIGHT('00000' + cp, 5) en SQL Server.

Con los teléfonos además se pierde el +. Si la columna lleva +52 o espacios, se queda como texto, que es lo que quieres.

Codificación: UTF-8, Windows-1252 y el BOM

Muñoz significa que un archivo UTF-8 se leyó como Windows-1252; Mu�oz, lo contrario. Excel en Windows guarda «CSV (delimitado por comas)» en la página de códigos del sistema; «CSV UTF-8 (delimitado por comas)» guarda en UTF-8 con BOM, y es la opción buena.

El BOM son tres bytes invisibles (EF BB BF) al principio. Los programas que no lo esperan lo pegan al primer nombre de columna, y acabas con una columna id que SELECT id no encuentra. El conversor lee los archivos como UTF-8 y descarta el BOM; si ves � en el panel de entrada, el archivo era Windows-1252 y toca volver a guardarlo. En destino, usa utf8mb4 en MySQL (el utf8 antiguo no admite emojis) y NVARCHAR en SQL Server.

Nombres de columna: backticks, corchetes y comillas dobles

MotorCitaLo que hay que saber
MySQL`fecha de alta`Las comillas dobles solo citan con ANSI_QUOTES
SQL Server[fecha de alta]También comillas dobles con QUOTED_IDENTIFIER ON
PostgreSQL"fecha de alta"Citado distingue mayúsculas: "Nombre" no es nombre
Oracle"FECHA"Sin citar, todo pasa a mayúsculas
SQLite"fecha de alta"Acepta backticks y corchetes por compatibilidad

Lo de PostgreSQL es lo que más guerra da: crea la tabla con "Nombre" y tendrás que escribirlo entre comillas en cada consulta. Por eso conviene pasar las cabeceras a minúsculas sin espacios. El conversor lo hace por defecto: «Fecha de alta» queda en fecha_de_alta, sin acentos, con la eñe como n y con prefijo c_ si empieza por número. Si desactivas la limpieza, los nombres originales salen citados según el motor.

Lotes, límites de cada motor y transacciones

Un INSERT multi-fila agrupa muchas filas en un solo VALUES (...), (...) y suele ser un orden de magnitud más rápido que uno por fila. Con límites: SQL Server admite como máximo 1000 filas por VALUES (error 10738, no configurable); MySQL no limita filas, pero la sentencia no puede superar max_allowed_packet (64 MB por defecto en MySQL 8, menos en muchos hostings); Oracle no admitía VALUES multi-fila antes de 23ai, y INSERT ALL ... SELECT * FROM dual funciona en todas las versiones.

En el conversor eliges uno por fila o multi-valores, con lotes de 1 a 1000 filas (100 por defecto), así que el límite de SQL Server nunca se supera; para Oracle genera INSERT ALL. Entre 100 y 500 filas por lote es un buen equilibrio: rápido, y si algo falla sabes en qué bloque mirar.

La transacción no la añade, y conviene que la pongas tú: sin ella, un fallo en la fila 8000 te deja 7999 filas dentro.

  • PostgreSQL: BEGIN; y COMMIT;. El DDL también es transaccional: hasta el CREATE TABLE se deshace.
  • MySQL: START TRANSACTION; y COMMIT; con InnoDB. Lanza el CREATE TABLE aparte, porque el DDL hace commit implícito.
  • SQL Server: SET XACT_ABORT ON; BEGIN TRANSACTION; y COMMIT;. Sin XACT_ABORT, un error de restricción solo cancela esa sentencia y el script sigue.
  • SQLite: BEGIN; y COMMIT;. Sin ella, cada INSERT sincroniza el archivo con el disco y todo va muchísimo más lento.

Un flujo que funciona

  1. Exporta como «CSV UTF-8» o suelta el .xlsx en el conversor; nada se sube a ningún servidor.
  2. Revisa los tipos detectados: fechas como fecha, importes como decimal, códigos postales como texto.
  3. Ajusta el CREATE TABLE: clave primaria y longitudes, que salen redondeadas al alza a propósito.
  4. Usa multi-valores en lotes de unos cientos, dentro de una transacción, y compara el COUNT(*) con las filas del archivo.

La herramienta de esta guía

CSV a SQL y otros formatos

Funciona dentro de tu navegador: nada de lo que pongas sale de esta página.

Convertir mi CSV o Excel a SQL

Más guías