DEV Community

Cover image for Webtabellen Converteren naar SQL INSERT-Statements
circobit
circobit

Posted on

Webtabellen Converteren naar SQL INSERT-Statements

Je hebt een tabel op een webpagina. Je hebt het nodig in je database.

De handmatige aanpak: kopiëren naar Excel, opschonen, CSV exporteren, handmatig CREATE TABLE schrijven, LOAD DATA of COPY gebruiken, fouten debuggen.

De betere aanpak: genereer direct complete SQL—CREATE TABLE met afgeleide types, INSERT-statements met juiste escaping.

Zo bouw je een webtabel-naar-SQL-converter.

Het Outputformaat

Een complete SQL-export moet bevatten:

-- Geëxporteerd met HTML Table Exporter PRO

CREATE TABLE producten (
  product_id INTEGER,
  naam TEXT,
  prijs REAL,
  op_voorraad TEXT
);

INSERT INTO producten (product_id, naam, prijs, op_voorraad) VALUES
  (1, 'Widget', 29.99, 'true'),
  (2, 'Gadget', 49.99, 'false'),
  (3, 'O''Brien''s Special', 19.99, 'true');
Enter fullscreen mode Exit fullscreen mode

Kernvereisten:

  1. Geldige tabelnaam (SQL-veilige identifier)
  2. Geldige kolomnamen (geen spaties, geen speciale tekens)
  3. Passende kolomtypen (INTEGER, REAL, TEXT)
  4. Correct ge-escapete waarden (enkele aanhalingstekens verdubbeld)
  5. NULL-afhandeling

Stap 1: Identifiers Opschonen

SQL-identifiers (tabel- en kolomnamen) hebben strikte regels:

  • Geen spaties of speciale tekens
  • Mag niet met een cijfer beginnen
  • Moet in kleine letters voor portabiliteit
function sanitizeSqlIdentifier(name, fallback) {
  let id = (name || "").toString().trim();

  if (!id) id = fallback || "col";

  id = id
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")  // Verwijder accenten (café → cafe)
    .toLowerCase()
    .replace(/[^a-z0-9_]+/g, "_")     // Vervang ongeldige tekens met underscore
    .replace(/^_+|_+$/g, "");          // Trim begin/eind-underscores

  // SQL-identifiers mogen niet met een cijfer beginnen
  if (/^[0-9]/.test(id)) {
    id = "_" + id;
  }

  if (!id) id = fallback || "col";
  return id;
}
Enter fullscreen mode Exit fullscreen mode

Voorbeelden:

  • "Product Naam" → "product_naam"
  • "Prijs (€)" → "prijs"
  • "2024 Omzet" → "_2024_omzet"
  • "Préço" → "preco"

Stap 2: Unieke Kolomnamen Genereren

Tabellen kunnen dubbele headers hebben. SQL kan geen dubbele kolomnamen hebben.

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;
  });
}
Enter fullscreen mode Exit fullscreen mode

Voorbeelden:

  • ["Naam", "Naam", "Waarde"] → ["naam", "naam_1", "waarde"]
  • ["", "", "Data"] → ["col_1", "col_2", "data"]

Stap 3: Kolomtypen Afleiden

SQL heeft drie hoofdtypen die we nodig hebben:

  • INTEGER: Gehele getallen
  • REAL: Decimale getallen
  • TEXT: Al het andere

De afleiding bemonstert waarden uit de kolom en kiest het meest specifieke type dat bij alle waarden past:

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++) {
    // Bemonstert maximaal 50 niet-lege waarden
    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;
    }

    // Controleer of alle waarden integers zijn
    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;
}
Enter fullscreen mode Exit fullscreen mode

Kernbeslissingen:

  • Bemonstert slechts 50 waarden voor prestaties bij grote tabellen
  • INTEGER is strikter dan REAL (geen decimalen toegestaan)
  • Als EEN waarde niet past, val terug op TEXT
  • Lege kolommen worden standaard TEXT

Stap 4: Waarden Escapen

SQL-strings vereisen escaping van enkele aanhalingstekens door ze te verdubbelen:

function sqlEscapeValue(raw, type) {
  // NULL-afhandeling
  if (raw == null) return "NULL";

  const v = String(raw).trim();
  if (v === "") return "NULL";

  // Voor numerieke typen, probeer een niet-geciteerd getal te retourneren
  if (type === "INTEGER" || type === "REAL") {
    const normalized = v.replace(",", ".");  // Verwerk EU decimaalformaat
    const num = Number(normalized);

    if (!Number.isNaN(num) && Number.isFinite(num)) {
      return normalized;
    }
    // Val door naar TEXT-afhandeling als het geen geldig getal is
  }

  // TEXT of fallback: escape enkele aanhalingstekens
  const escaped = v.replace(/'/g, "''");
  return `'${escaped}'`;
}
Enter fullscreen mode Exit fullscreen mode

Voorbeelden:

  • "Hallo"'Hallo'
  • "O'Brien"'O''Brien'
  • "Het is \"geciteerd\""'Het is "geciteerd"'
  • 123 (INTEGER) → 123
  • 45.67 (REAL) → 45.67
  • ""NULL
  • nullNULL

Stap 5: De Volledige SQL Genereren

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 "";

  // Genereer veilige kolomnamen
  const columnNames = generateColumnNames(headerRow);

  // Leid kolomtypen af
  const types = inferSqlColumnTypes(rows, headerRowIndex);

  // Genereer veilige tabelnaam
  const rawTableName = tableInfo.slug || tableInfo.name || "tabel";
  let tableName = sanitizeSqlIdentifier(rawTableName, "tabel");
  if (!tableName) tableName = "tabel_export";

  // BOUW CREATE TABLE
  const createLines = columnNames.map((col, i) => 
    `  ${col} ${types[i] || "TEXT"}`
  );
  const createStmt = `CREATE TABLE ${tableName} (\n${createLines.join(",\n")}\n);`;

  // BOUW INSERT-STATEMENTS
  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(", ")})`;
  });

  // COMBINEER
  let sql = `-- Geëxporteerd met HTML Table Exporter PRO\n\n${createStmt}\n\n`;

  if (valueLines.length) {
    sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
  }

  return sql;
}
Enter fullscreen mode Exit fullscreen mode

Voorbeeldoutput

Invoertabel:

Product Prijs Aant
Widget 29.99 100
O'Brien's 19.99 50

Output:

-- Geëxporteerd met HTML Table Exporter PRO

CREATE TABLE producten (
  product TEXT,
  prijs REAL,
  aant INTEGER
);

INSERT INTO producten (product, prijs, aant) VALUES
  ('Widget', 29.99, 100),
  ('O''Brien''s', 19.99, 50);
Enter fullscreen mode Exit fullscreen mode

Randgevallen Afhandelen

Lege Tabellen

if (!rows.length) return "";
if (!headerRow) return "";
Enter fullscreen mode Exit fullscreen mode

Retourneer een lege string in plaats van ongeldige SQL.

Kolommen met Alleen NULL

Kolommen met alleen lege/null-waarden krijgen het TEXT-type. De afleidingsfunctie verwerkt dit:

if (values.length === 0) {
  types[col] = "TEXT";
  continue;
}
Enter fullscreen mode Exit fullscreen mode

Gemengde Getalformaten

De escaper normaliseert komma-decimalen:

const normalized = v.replace(",", ".");
Enter fullscreen mode Exit fullscreen mode

"1,234.56" blijft zoals het is. "1.234,56" (EU-formaat) moet genormaliseerd worden vóór het de SQL-generator bereikt—dat is de taak van de opschoningspresets.

Zeer Lange Waarden

TEXT-kolommen in SQLite/PostgreSQL verwerken willekeurige lengte. Geen afkapping nodig. Voor MySQL's VARCHAR-limieten moet je de lengte specificeren of expliciet TEXT gebruiken.

Database-compatibiliteit

De gegenereerde SQL is opzettelijk eenvoudig:

Feature SQLite PostgreSQL MySQL
CREATE TABLE
INTEGER/REAL/TEXT
Multi-rij INSERT
Enkele quote escape

Voor databasespecifieke features (constraints, indexen, schema's) breid je de generator uit. Voor eenvoudige data-import werkt dit overal.

De Export Gebruiken

# SQLite
sqlite3 mijndb.db < export.sql

# PostgreSQL
psql -d mijndb -f export.sql

# MySQL
mysql mijndb < export.sql
Enter fullscreen mode Exit fullscreen mode

Of plak direct in je databaseclient.

Lees voor meer over het exporteren van tabellen naar andere formaten onze gids over de beste Chrome-extensies voor het exporteren van tabellen.


Wil je SQL-exports zonder code te schrijven? Meer informatie op gauchogrid.com/nl/html-table-exporter of probeer het gratis in de Chrome Web Store.

Top comments (0)