웹페이지에 테이블이 있습니다. 데이터베이스에 넣어야 합니다.
수동 접근 방식: Excel에 복사, 정리, CSV 내보내기, 직접 CREATE TABLE 작성, LOAD DATA 또는 COPY 사용, 오류 디버깅.
더 나은 접근 방식: 추론된 타입이 포함된 CREATE TABLE과 적절한 이스케이프가 적용된 INSERT 문을 포함한 완전한 SQL을 직접 생성.
웹 테이블을 SQL로 변환하는 방법을 알아보겠습니다.
출력 형식
완전한 SQL 내보내기에는 다음이 포함되어야 합니다:
-- 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');
핵심 요구사항:
- 유효한 테이블 이름 (SQL 안전 식별자)
- 유효한 열 이름 (공백 없음, 특수 문자 없음)
- 적절한 열 타입 (INTEGER, REAL, TEXT)
- 올바르게 이스케이프된 값 (작은따옴표 두 번)
- NULL 처리
1단계: 식별자 정제
SQL 식별자(테이블 및 열 이름)에는 엄격한 규칙이 있습니다:
- 공백이나 특수 문자 없음
- 숫자로 시작할 수 없음
- 이식성을 위해 소문자 사용
function sanitizeSqlIdentifier(name, fallback) {
let id = (name || "").toString().trim();
if (!id) id = fallback || "col";
id = id
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "") // 악센트 제거 (café → cafe)
.toLowerCase()
.replace(/[^a-z0-9_]+/g, "_") // 유효하지 않은 문자를 밑줄로 대체
.replace(/^_+|_+$/g, ""); // 앞뒤 밑줄 제거
// SQL 식별자는 숫자로 시작할 수 없음
if (/^[0-9]/.test(id)) {
id = "_" + id;
}
if (!id) id = fallback || "col";
return id;
}
예시:
- "Product Name" → "product_name"
- "Price ($)" → "price"
- "2024 Revenue" → "_2024_revenue"
- "Préço" → "preco"
2단계: 고유한 열 이름 생성
테이블에 중복 헤더가 있을 수 있습니다. SQL에서는 중복 열 이름을 사용할 수 없습니다.
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;
});
}
예시:
- ["Name", "Name", "Value"] → ["name", "name_1", "value"]
- ["", "", "Data"] → ["col_1", "col_2", "data"]
3단계: 열 타입 추론
SQL에서 주로 사용하는 세 가지 타입:
- INTEGER: 정수
- REAL: 소수
- TEXT: 나머지 전부
추론은 열의 값을 샘플링하고 모든 값에 맞는 가장 구체적인 타입을 선택합니다:
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++) {
// 비어 있지 않은 값 최대 50개 샘플링
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;
}
// 모든 값이 정수인지 확인
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;
}
핵심 결정:
- 대규모 테이블 성능을 위해 50개 값만 샘플링
- INTEGER가 REAL보다 엄격 (소수 불허)
- 어떤 값이든 맞지 않으면 TEXT로 폴백
- 빈 열은 TEXT로 기본값
4단계: 값 이스케이프
SQL 문자열에서는 작은따옴표를 두 번으로 이스케이프해야 합니다:
function sqlEscapeValue(raw, type) {
// NULL 처리
if (raw == null) return "NULL";
const v = String(raw).trim();
if (v === "") return "NULL";
// 숫자 타입의 경우 따옴표 없는 숫자 반환 시도
if (type === "INTEGER" || type === "REAL") {
const normalized = v.replace(",", "."); // 유럽식 소수점 처리
const num = Number(normalized);
if (!Number.isNaN(num) && Number.isFinite(num)) {
return normalized;
}
// 유효한 숫자가 아니면 TEXT 처리로 진행
}
// TEXT 또는 폴백: 작은따옴표 이스케이프
const escaped = v.replace(/'/g, "''");
return `'${escaped}'`;
}
예시:
-
"Hello"→'Hello' -
"O'Brien"→'O''Brien' -
"It's \"quoted\""→'It''s "quoted"' -
123(INTEGER) →123 -
45.67(REAL) →45.67 -
""→NULL -
null→NULL
5단계: 완전한 SQL 생성
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 "";
// 안전한 열 이름 생성
const columnNames = generateColumnNames(headerRow);
// 열 타입 추론
const types = inferSqlColumnTypes(rows, headerRowIndex);
// 안전한 테이블 이름 생성
const rawTableName = tableInfo.slug || tableInfo.name || "table";
let tableName = sanitizeSqlIdentifier(rawTableName, "table");
if (!tableName) tableName = "table_export";
// CREATE TABLE 구성
const createLines = columnNames.map((col, i) =>
` ${col} ${types[i] || "TEXT"}`
);
const createStmt = `CREATE TABLE ${tableName} (\n${createLines.join(",\n")}\n);`;
// 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(", ")})`;
});
// 결합
let sql = `-- HTML Table Exporter PRO에서 내보냄\n\n${createStmt}\n\n`;
if (valueLines.length) {
sql += `${insertHeader}\n${valueLines.join(",\n")};\n`;
}
return sql;
}
출력 예시
입력 테이블:
| Product | Price | Qty |
|---|---|---|
| Widget | 29.99 | 100 |
| O'Brien's | 19.99 | 50 |
출력:
-- HTML Table Exporter PRO에서 내보냄
CREATE TABLE products (
product TEXT,
price REAL,
qty INTEGER
);
INSERT INTO products (product, price, qty) VALUES
('Widget', 29.99, 100),
('O''Brien''s', 19.99, 50);
엣지 케이스 처리
빈 테이블
if (!rows.length) return "";
if (!headerRow) return "";
유효하지 않은 SQL 대신 빈 문자열을 반환합니다.
모든 값이 NULL인 열
빈/null 값만 있는 열은 TEXT 타입을 받습니다. 추론 함수가 이를 처리합니다:
if (values.length === 0) {
types[col] = "TEXT";
continue;
}
혼합된 숫자 형식
이스케이퍼가 쉼표 소수점을 정규화합니다:
const normalized = v.replace(",", ".");
"1,234.56"은 그대로 유지됩니다. "1.234,56" (유럽식 형식)은 SQL 생성기에 도달하기 전에 정규화되어야 합니다—이것은 정제 프리셋의 역할입니다.
매우 긴 값
SQLite/PostgreSQL의 TEXT 열은 임의의 길이를 처리합니다. 잘라내기가 필요 없습니다. MySQL의 VARCHAR 제한의 경우 길이를 지정하거나 명시적으로 TEXT를 사용해야 합니다.
데이터베이스 호환성
생성된 SQL은 의도적으로 단순합니다:
| 기능 | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| CREATE TABLE | ✅ | ✅ | ✅ |
| INTEGER/REAL/TEXT | ✅ | ✅ | ✅ |
| 다중 행 INSERT | ✅ | ✅ | ✅ |
| 작은따옴표 이스케이프 | ✅ | ✅ | ✅ |
데이터베이스별 기능(제약 조건, 인덱스, 스키마)의 경우 생성기를 확장하면 됩니다. 기본 데이터 임포트에는 모든 곳에서 작동합니다.
내보내기 사용
# SQLite
sqlite3 mydb.db < export.sql
# PostgreSQL
psql -d mydb -f export.sql
# MySQL
mysql mydb < export.sql
또는 데이터베이스 클라이언트에 직접 붙여넣기.
SQL이 필요 없는 CSV 내보내기 경로에 대해서는 HTML 테이블을 CSV로 내보내기 가이드를 참조하세요.
코드 없이 SQL 내보내기가 필요하신가요? gauchogrid.com/ko/html-table-exporter에서 자세히 알아보거나 Chrome 웹 스토어에서 무료로 사용해 보세요.
Top comments (0)