Você tem uma tabela em uma página web. Precisa dela no seu banco de dados.
A abordagem manual: copiar para o Excel, limpar, exportar CSV, escrever CREATE TABLE à mão, usar LOAD DATA ou COPY, debugar os erros.
A abordagem melhor: gerar SQL completo diretamente — CREATE TABLE com tipos inferidos, instruções INSERT com escape adequado.
Veja como construir um conversor de tabela web para SQL.
O Formato de Saída
Uma exportação SQL completa deve incluir:
-- Exportado pelo HTML Table Exporter PRO
CREATE TABLE produtos (
produto_id INTEGER,
nome TEXT,
preco REAL,
em_estoque TEXT
);
INSERT INTO produtos (produto_id, nome, preco, em_estoque) VALUES
(1, 'Widget', 29.99, 'true'),
(2, 'Gadget', 49.99, 'false'),
(3, 'Peça do O''Brien', 19.99, 'true');
Requisitos-chave:
- Nome de tabela válido (identificador SQL seguro)
- Nomes de colunas válidos (sem espaços, sem caracteres especiais)
- Tipos de coluna apropriados (INTEGER, REAL, TEXT)
- Valores com escape adequado (aspas simples duplicadas)
- Tratamento de NULL
Passo 1: Sanitizando Identificadores
Identificadores SQL (nomes de tabelas e colunas) têm regras estritas:
- Sem espaços ou caracteres especiais
- Não pode começar com dígito
- Deve ser minúsculo para portabilidade
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, "_") // Substituir chars inválidos por underscore
.replace(/^_+|_+$/g, ""); // Remover underscores iniciais/finais
// Identificadores SQL não podem começar com dígito
if (/^[0-9]/.test(id)) {
id = "_" + id;
}
if (!id) id = fallback || "col";
return id;
}
Exemplos:
- "Nome do Produto" → "nome_do_produto"
- "Preço (R$)" → "preco_r"
- "2024 Receita" → "_2024_receita"
- "Préço" → "preco"
Passo 2: Gerando Nomes de Colunas Únicos
Tabelas podem ter cabeçalhos duplicados. SQL não pode ter nomes de colunas 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;
});
}
Exemplos:
- ["Nome", "Nome", "Valor"] → ["nome", "nome_1", "valor"]
- ["", "", "Dados"] → ["col_1", "col_2", "dados"]
Passo 3: Inferindo Tipos de Colunas
SQL tem três tipos principais que nos interessam:
- INTEGER: Números inteiros
- REAL: Números decimais
- TEXT: Todo o resto
A inferência amostra valores da coluna e escolhe o tipo mais específico que serve para todos os 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++) {
// Amostrar até 50 valores não vazios
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 se todos os valores são inteiros
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;
}
Decisões-chave:
- Amostrar apenas 50 valores para performance em tabelas grandes
- INTEGER é mais restrito que REAL (sem decimais)
- Se QUALQUER valor não corresponder, cai para TEXT
- Colunas vazias recebem TEXT por padrão
Passo 4: Escapando Valores
Strings SQL requerem escape de aspas simples duplicando-as:
function sqlEscapeValue(raw, type) {
// Tratamento de NULL
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "") return "NULL";
// Para tipos numéricos, tentar retornar número sem aspas
if (type === "INTEGER" || type === "REAL") {
const normalized = v.replace(",", "."); // Tratar formato decimal UE
const num = Number(normalized);
if (!Number.isNaN(num) && Number.isFinite(num)) {
return normalized;
}
// Cai para tratamento TEXT se não for número válido
}
// TEXT ou fallback: escapar aspas simples
const escaped = v.replace(/'/g, "''");
return `'${escaped}'`;
}
Exemplos:
-
"Olá"→'Olá' -
"O'Brien"→'O''Brien' -
"Está \"entre aspas\""→'Está "entre aspas"' -
123(INTEGER) →123 -
45.67(REAL) →45.67 -
""→NULL -
null→NULL
Passo 5: Gerando o 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 "";
// Gerar nomes de colunas seguros
const columnNames = generateColumnNames(headerRow);
// Inferir tipos de colunas
const types = inferSqlColumnTypes(rows, headerRowIndex);
// Gerar nome de tabela seguro
const rawTableName = tableInfo.slug || tableInfo.name || "tabela";
let tableName = sanitizeSqlIdentifier(rawTableName, "tabela");
if (!tableName) tableName = "tabela_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 INSTRUÇÕES 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 pelo HTML Table Exporter PRO\n\n${createStmt}\n\n`;
if (valueLines.length) {
sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
}
return sql;
}
Exemplo de Saída
Tabela de entrada:
| Produto | Preço | Qtd |
|---|---|---|
| Widget | 29.99 | 100 |
| O'Brien's | 19.99 | 50 |
Saída:
-- Exportado pelo HTML Table Exporter PRO
CREATE TABLE produtos (
produto TEXT,
preco REAL,
qtd INTEGER
);
INSERT INTO produtos (produto, preco, qtd) VALUES
('Widget', 29.99, 100),
('O''Brien''s', 19.99, 50);
Tratando Casos Especiais
Tabelas Vazias
if (!rows.length) return "";
if (!headerRow) return "";
Retornar string vazia em vez de SQL inválido.
Colunas Todas NULL
Colunas com apenas valores vazios/null recebem tipo TEXT. A função de inferência trata isso:
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
Formatos Numéricos Mistos
O escaper normaliza decimais com vírgula:
const normalized = v.replace(",", ".");
"1,234.56" permanece como está. "1.234,56" (formato UE) deve ser normalizado antes de chegar ao gerador SQL — esse é o trabalho dos presets de limpeza.
Valores Muito Longos
Colunas TEXT no SQLite/PostgreSQL lidam com tamanho arbitrário. Nenhuma truncação necessária. Para limites de VARCHAR do MySQL, você precisaria especificar comprimento ou usar TEXT explicitamente.
Compatibilidade entre Bancos de Dados
O SQL gerado é intencionalmente simples:
| Recurso | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| CREATE TABLE | ✅ | ✅ | ✅ |
| INTEGER/REAL/TEXT | ✅ | ✅ | ✅ |
| INSERT multi-linhas | ✅ | ✅ | ✅ |
| Escape aspas simples | ✅ | ✅ | ✅ |
Para recursos específicos de banco (constraints, índices, schemas), você estenderia o gerador. Para importação básica de dados, funciona em qualquer lugar.
Usando a Exportação
# SQLite
sqlite3 meubanco.db < exportacao.sql
# PostgreSQL
psql -d meubanco -f exportacao.sql
# MySQL
mysql meubanco < exportacao.sql
Ou cole diretamente no seu cliente de banco de dados.
Para saber mais sobre como exportar tabelas web para diferentes formatos, veja nosso guia sobre a melhor extensão Chrome para copiar tabelas para Excel.
Precisa de exportações SQL sem escrever código? Saiba mais em gauchogrid.com/pt-br/html-table-exporter ou experimente gratuitamente na Chrome Web Store.
Top comments (0)