Tienes una tabla en una página web. La necesitas en tu base de datos.
El enfoque manual: copiar a Excel, limpiar, exportar CSV, escribir CREATE TABLE a mano, usar LOAD DATA o COPY, depurar los errores.
El enfoque mejor: generar SQL completo directamente — CREATE TABLE con tipos inferidos, sentencias INSERT con escape adecuado.
Aquí te muestro cómo construir un conversor de tabla web a SQL.
El Formato de Salida
Una exportación SQL completa debería incluir:
-- Exportado con HTML Table Exporter PRO
CREATE TABLE products (
product_id INTEGER,
name TEXT,
price REAL,
in_stock TEXT
);
INSERT INTO products (product_id, name, price, in_stock) VALUES
(1, 'Widget', 29.99, 'true'),
(2, 'Gadget', 49.99, 'false'),
(3, 'O''Brien''s Special', 19.99, 'true');
Requisitos clave:
- Nombre de tabla válido (identificador SQL seguro)
- Nombres de columna válidos (sin espacios, sin caracteres especiales)
- Tipos de columna apropiados (INTEGER, REAL, TEXT)
- Valores correctamente escapados (comillas simples duplicadas)
- Manejo de NULL
Paso 1: Sanitizar Identificadores
Los identificadores SQL (nombres de tabla y columna) tienen reglas estrictas:
- Sin espacios ni caracteres especiales
- No pueden empezar con un dígito
- Deberían ser minúsculas para portabilidad
function sanitizeSqlIdentifier(name, fallback) {
let id = (name || "").toString().trim();
if (!id) id = fallback || "col";
id = id
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "") // Remover acentos (café → cafe)
.toLowerCase()
.replace(/[^a-z0-9_]+/g, "_") // Reemplazar caracteres inválidos con guión bajo
.replace(/^_+|_+$/g, ""); // Recortar guiones bajos al inicio/final
// Los identificadores SQL no pueden empezar con un dígito
if (/^[0-9]/.test(id)) {
id = "_" + id;
}
if (!id) id = fallback || "col";
return id;
}
Ejemplos:
- "Nombre Producto" → "nombre_producto"
- "Precio ($)" → "precio"
- "2024 Ingresos" → "_2024_ingresos"
- "Préço" → "preco"
Paso 2: Generar Nombres de Columna Únicos
Las tablas pueden tener encabezados duplicados. SQL no puede tener nombres de columna duplicados.
function generateColumnNames(headerRow) {
const usedNames = new Set();
return headerRow.map((header, index) => {
const base = sanitizeSqlIdentifier(header, `col_${index + 1}`);
let candidate = base;
let counter = 1;
while (usedNames.has(candidate)) {
candidate = `${base}_${counter}`;
counter++;
}
usedNames.add(candidate);
return candidate;
});
}
Ejemplos:
- ["Nombre", "Nombre", "Valor"] → ["nombre", "nombre_1", "valor"]
- ["", "", "Datos"] → ["col_1", "col_2", "datos"]
Paso 3: Inferir Tipos de Columna
SQL tiene tres tipos principales que nos importan:
- INTEGER: Números enteros
- REAL: Números decimales
- TEXT: Todo lo demás
La inferencia muestrea valores de la columna y elige el tipo más específico que se ajuste a todos los valores:
function inferSqlColumnTypes(rows, headerRowIndex = 0) {
const headerRow = rows[headerRowIndex] || [];
const dataRows = rows.slice(headerRowIndex + 1);
const colCount = headerRow.length;
const types = new Array(colCount).fill("TEXT");
for (let col = 0; col < colCount; col++) {
// Muestrear hasta 50 valores no vacíos
const values = [];
for (let r = 0; r < dataRows.length && values.length < 50; r++) {
const cell = dataRows[r][col];
const v = cell != null ? String(cell).trim() : "";
if (v !== "") values.push(v);
}
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
// Verificar si todos los valores son enteros
let allInt = true;
let allNumeric = true;
for (const v of values) {
if (!/^[-+]?\d+$/.test(v)) {
allInt = false;
}
if (!/^[-+]?\d+([.,]\d+)?$/.test(v)) {
allNumeric = false;
}
}
if (allInt) {
types[col] = "INTEGER";
} else if (allNumeric) {
types[col] = "REAL";
} else {
types[col] = "TEXT";
}
}
return types;
}
Decisiones clave:
- Muestrear solo 50 valores para rendimiento en tablas grandes
- INTEGER es más estricto que REAL (no se permiten decimales)
- Si CUALQUIER valor no coincide, caer a TEXT
- Las columnas vacías tienen TEXT por defecto
Paso 4: Escapar Valores
Las cadenas SQL requieren escapar comillas simples duplicándolas:
function sqlEscapeValue(raw, type) {
// Manejo de NULL
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "") return "NULL";
// Para tipos numéricos, intentar devolver número sin comillas
if (type === "INTEGER" || type === "REAL") {
const normalized = v.replace(",", "."); // Manejar formato decimal UE
const num = Number(normalized);
if (!Number.isNaN(num) && Number.isFinite(num)) {
return normalized;
}
// Caer al manejo TEXT si no es un número válido
}
// TEXT o fallback: escapar comillas simples
const escaped = v.replace(/'/g, "''");
return `'${escaped}'`;
}
Ejemplos:
-
"Hola"→'Hola' -
"O'Brien"→'O''Brien' -
"Es \"citado\""→'Es "citado"' -
123(INTEGER) →123 -
45.67(REAL) →45.67 -
""→NULL -
null→NULL
Paso 5: Generar el SQL Completo
function tableToSqlString(tableInfo) {
const rows = tableInfo.rows || [];
if (!rows.length) return "";
const headerRowIndex = tableInfo.headerRowIndex || 0;
const headerRow = rows[headerRowIndex];
const dataRows = rows.slice(headerRowIndex + 1);
if (!headerRow) return "";
// Generar nombres de columna seguros
const columnNames = generateColumnNames(headerRow);
// Inferir tipos de columna
const types = inferSqlColumnTypes(rows, headerRowIndex);
// Generar nombre de tabla seguro
const rawTableName = tableInfo.slug || tableInfo.name || "table";
let tableName = sanitizeSqlIdentifier(rawTableName, "table");
if (!tableName) tableName = "table_export";
// CONSTRUIR CREATE TABLE
const createLines = columnNames.map((col, i) =>
` ${col} ${types[i] || "TEXT"}`
);
const createStmt = `CREATE TABLE ${tableName} (\n${createLines.join(",\n")}\n);`;
// CONSTRUIR SENTENCIAS INSERT
const insertHeader = `INSERT INTO ${tableName} (${columnNames.join(", ")}) VALUES`;
const valueLines = dataRows.map(row => {
const values = columnNames.map((_, i) => {
const cell = row[i];
const type = types[i] || "TEXT";
return sqlEscapeValue(cell, type);
});
return ` (${values.join(", ")})`;
});
// COMBINAR
let sql = `-- Exportado con HTML Table Exporter PRO\n\n${createStmt}\n\n`;
if (valueLines.length) {
sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
}
return sql;
}
Ejemplo de Salida
Tabla de entrada:
| Producto | Precio | Cant |
|---|---|---|
| Widget | 29.99 | 100 |
| O'Brien's | 19.99 | 50 |
Salida:
-- Exportado con HTML Table Exporter PRO
CREATE TABLE products (
producto TEXT,
precio REAL,
cant INTEGER
);
INSERT INTO products (producto, precio, cant) VALUES
('Widget', 29.99, 100),
('O''Brien''s', 19.99, 50);
Manejando Casos Extremos
Tablas Vacías
if (!rows.length) return "";
if (!headerRow) return "";
Devolver string vacío en vez de SQL inválido.
Columnas Completamente NULL
Las columnas con solo valores vacíos/null reciben tipo TEXT. La función de inferencia maneja esto:
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
Formatos Numéricos Mixtos
El escapador normaliza decimales con coma:
const normalized = v.replace(",", ".");
"1,234.56" queda igual. "1.234,56" (formato UE) debería normalizarse antes de llegar al generador SQL — ese es el trabajo de los presets de limpieza.
Valores Muy Largos
Las columnas TEXT en SQLite/PostgreSQL manejan largo arbitrario. No se necesita truncar. Para los límites de VARCHAR de MySQL, necesitarás especificar el largo o usar TEXT explícitamente.
Compatibilidad con Bases de Datos
El SQL generado es intencionalmente simple:
| Funcionalidad | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| CREATE TABLE | ✅ | ✅ | ✅ |
| INTEGER/REAL/TEXT | ✅ | ✅ | ✅ |
| INSERT multi-fila | ✅ | ✅ | ✅ |
| Escape comilla simple | ✅ | ✅ | ✅ |
Para funcionalidades específicas de base de datos (constraints, índices, esquemas), extenderías el generador. Para importación básica de datos, esto funciona en todas partes.
Usando la Exportación
# SQLite
sqlite3 mydb.db < export.sql
# PostgreSQL
psql -d mydb -f export.sql
# MySQL
mysql mydb < export.sql
O pégalo directamente en tu cliente de base de datos.
Para más sobre la ruta de exportación CSV (cuando no necesitas SQL), consulta nuestra guía sobre exportar tablas HTML a CSV.
¿Necesitas exportaciones SQL sin escribir código? Conoce más en gauchogrid.com/es/html-table-exporter o pruébalo gratis en la Chrome Web Store.
Top comments (0)