Man hat eine Tabelle auf einer Webseite. Man braucht sie in der Datenbank.
Der manuelle Weg: nach Excel kopieren, bereinigen, als CSV exportieren, CREATE TABLE von Hand schreiben, LOAD DATA oder COPY verwenden, Fehler debuggen.
Der bessere Weg: komplettes SQL direkt generieren — CREATE TABLE mit erkannten Typen, INSERT-Anweisungen mit korrektem Escaping.
So baut man einen Web-Tabelle-zu-SQL-Konverter.
Das Ausgabeformat
Ein vollständiger SQL-Export sollte enthalten:
-- Exportiert mit HTML Table Exporter PRO
CREATE TABLE produkte (
produkt_id INTEGER,
name TEXT,
preis REAL,
auf_lager TEXT
);
INSERT INTO produkte (produkt_id, name, preis, auf_lager) VALUES
(1, 'Widget', 29.99, 'true'),
(2, 'Gadget', 49.99, 'false'),
(3, 'O''Brien''s Spezial', 19.99, 'true');
Anforderungen:
- Gültiger Tabellenname (SQL-sicherer Bezeichner)
- Gültige Spaltennamen (keine Leerzeichen, keine Sonderzeichen)
- Passende Spaltentypen (INTEGER, REAL, TEXT)
- Korrekt escapte Werte (einfache Anführungszeichen verdoppelt)
- NULL-Behandlung
Schritt 1: Bezeichner bereinigen
SQL-Bezeichner (Tabellen- und Spaltennamen) haben strenge Regeln:
- Keine Leerzeichen oder Sonderzeichen
- Darf nicht mit einer Ziffer beginnen
- Sollte für Portabilität kleingeschrieben sein
function sanitizeSqlIdentifier(name, fallback) {
let id = (name || "").toString().trim();
if (!id) id = fallback || "col";
id = id
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "") // Akzente entfernen (café → cafe)
.toLowerCase()
.replace(/[^a-z0-9_]+/g, "_") // Ungültige Zeichen durch Unterstrich ersetzen
.replace(/^_+|_+$/g, ""); // Führende/nachfolgende Unterstriche entfernen
// SQL-Bezeichner dürfen nicht mit einer Ziffer beginnen
if (/^[0-9]/.test(id)) {
id = "_" + id;
}
if (!id) id = fallback || "col";
return id;
}
Beispiele:
- „Produktname" → „produktname"
- „Preis (€)" → „preis"
- „2024 Umsatz" → „_2024_umsatz"
- „Préço" → „preco"
Schritt 2: Eindeutige Spaltennamen generieren
Tabellen können doppelte Header haben. SQL kann keine doppelten Spaltennamen haben.
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;
});
}
Beispiele:
- ["Name", "Name", "Wert"] → ["name", "name_1", "wert"]
- ["", "", "Daten"] → ["col_1", "col_2", "daten"]
Schritt 3: Spaltentypen erkennen
SQL hat drei Haupttypen, die uns interessieren:
- INTEGER: Ganzzahlen
- REAL: Dezimalzahlen
- TEXT: Alles andere
Die Erkennung prüft Stichproben aus der Spalte und wählt den spezifischsten Typ, der zu allen Werten passt:
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++) {
// Bis zu 50 nicht-leere Werte als Stichprobe
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;
}
// Prüfen ob alle Werte ganzzahlig sind
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;
}
Wichtige Entscheidungen:
- Nur 50 Werte als Stichprobe für Performance bei großen Tabellen
- INTEGER ist strenger als REAL (keine Dezimalstellen erlaubt)
- Wenn IRGENDEIN Wert nicht passt, Fallback auf TEXT
- Leere Spalten bekommen standardmäßig TEXT
Schritt 4: Werte escapen
SQL-Strings erfordern das Escaping einfacher Anführungszeichen durch Verdopplung:
function sqlEscapeValue(raw, type) {
// NULL-Behandlung
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "") return "NULL";
// Für numerische Typen unquotierte Zahl zurückgeben
if (type === "INTEGER" || type === "REAL") {
const normalized = v.replace(",", "."); // EU-Dezimalformat behandeln
const num = Number(normalized);
if (!Number.isNaN(num) && Number.isFinite(num)) {
return normalized;
}
// Durchfallen zu TEXT-Behandlung wenn keine gültige Zahl
}
// TEXT oder Fallback: einfache Anführungszeichen escapen
const escaped = v.replace(/'/g, "''");
return `'${escaped}'`;
}
Beispiele:
-
"Hallo"→'Hallo' -
"O'Brien"→'O''Brien' -
"Er sagte 'ja'"→'Er sagte ''ja''' -
123(INTEGER) →123 -
45.67(REAL) →45.67 -
""→NULL -
null→NULL
Schritt 5: Das vollständige SQL generieren
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 "";
// Sichere Spaltennamen generieren
const columnNames = generateColumnNames(headerRow);
// Spaltentypen erkennen
const types = inferSqlColumnTypes(rows, headerRowIndex);
// Sicheren Tabellennamen generieren
const rawTableName = tableInfo.slug || tableInfo.name || "table";
let tableName = sanitizeSqlIdentifier(rawTableName, "table");
if (!tableName) tableName = "table_export";
// CREATE TABLE BAUEN
const createLines = columnNames.map((col, i) =>
` ${col} ${types[i] || "TEXT"}`
);
const createStmt = `CREATE TABLE ${tableName} (\n${createLines.join(",\n")}\n);`;
// INSERT-ANWEISUNGEN BAUEN
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(", ")})`;
});
// KOMBINIEREN
let sql = `-- Exportiert mit HTML Table Exporter PRO\n\n${createStmt}\n\n`;
if (valueLines.length) {
sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
}
return sql;
}
Beispielausgabe
Eingabetabelle:
| Produkt | Preis | Menge |
|---|---|---|
| Widget | 29.99 | 100 |
| O'Brien's | 19.99 | 50 |
Ausgabe:
-- Exportiert mit HTML Table Exporter PRO
CREATE TABLE produkte (
produkt TEXT,
preis REAL,
menge INTEGER
);
INSERT INTO produkte (produkt, preis, menge) VALUES
('Widget', 29.99, 100),
('O''Brien''s', 19.99, 50);
Sonderfälle behandeln
Leere Tabellen
if (!rows.length) return "";
if (!headerRow) return "";
Leeren String statt ungültigem SQL zurückgeben.
Komplett-NULL-Spalten
Spalten mit nur leeren/null-Werten bekommen den Typ TEXT. Die Erkennungsfunktion behandelt das:
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
Gemischte Zahlenformate
Der Escaper normalisiert Komma-Dezimalzeichen:
const normalized = v.replace(",", ".");
„1,234.56" bleibt unverändert. „1.234,56" (EU-Format) sollte vor Erreichen des SQL-Generators normalisiert werden — das ist Aufgabe der Bereinigungsprofile.
Sehr lange Werte
TEXT-Spalten in SQLite/PostgreSQL verarbeiten beliebige Länge. Kein Abschneiden nötig. Für MySQLs VARCHAR-Limits müsste man die Länge angeben oder explizit TEXT verwenden.
Datenbank-Kompatibilität
Das generierte SQL ist bewusst einfach gehalten:
| Feature | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| CREATE TABLE | ✅ | ✅ | ✅ |
| INTEGER/REAL/TEXT | ✅ | ✅ | ✅ |
| Multi-Row INSERT | ✅ | ✅ | ✅ |
| Einfache-Anführungszeichen-Escaping | ✅ | ✅ | ✅ |
Für datenbankspezifische Features (Constraints, Indizes, Schemas) würde man den Generator erweitern. Für den einfachen Datenimport funktioniert das überall.
Den Export verwenden
# SQLite
sqlite3 mydb.db < export.sql
# PostgreSQL
psql -d mydb -f export.sql
# MySQL
mysql mydb < export.sql
Oder direkt in den Datenbank-Client einfügen.
Für mehr zum CSV-Export-Weg (wenn kein SQL benötigt wird), siehe unseren Leitfaden zu den besten Chrome-Erweiterungen zum Exportieren von Tabellen.
SQL-Exporte ohne Code? Erfahren Sie mehr auf gauchogrid.com/de/html-table-exporter oder probieren Sie es kostenlos im Chrome Web Store.
Top comments (0)