CSV 转 SQL
从 CSV 数据生成 SQL INSERT 语句
CSV 输入
SQL 输出
什么是 CSV 转 SQL 转换?
CSV 转 SQL 转换将逗号分隔值转换为关系型数据库可执行的结构化查询语言语句。最常见的输出形式是一对语句:一条定义列的 CREATE TABLE 语句,以及一条或多条将 CSV 文件各行数据填入相应列的 INSERT INTO 语句。
CSV 文件遵循 RFC 4180 规范:每行为一条记录,字段由分隔符(通常为逗号)分隔,包含分隔符或换行符的字段用双引号括起。SQL 则是管理 PostgreSQL、MySQL、SQLite 和 SQL Server 等系统中数据的标准语言。在这两种格式之间进行转换,是软件开发中最常见的数据导入任务之一。
一个合格的 CSV 转 SQL 转换器需要处理引用规则、对值中的单引号进行转义(按 SQL 标准将其加倍为 '')、将列标题映射为合法的 SQL 标识符,并可选择性地推断数据类型。除基本转义外,优质转换器还会规范化列名(将空格和连字符替换为下划线),并将输出包裹在事务块中——这样导入失败时可以干净回滚,而不会在表中留下不完整的数据。若不借助转换器,即便只有几百行数据,手工编写 INSERT 语句也既容易出错又效率低下。
为什么使用 CSV 转 SQL 转换器?
从电子表格数据手工编写 SQL INSERT 语句既繁琐又容易引入语法错误。转换器可自动完成重复性工作,让你专注于数据库结构设计和数据验证。
CSV 转 SQL 使用场景
SQL 数据类型参考
将 CSV 转换为 SQL 时,由于 CSV 不携带类型元数据,所有列默认使用 TEXT 类型。如果你已知列的类型,可以在 CREATE TABLE 输出中替换 TEXT。下表列出最常用的 SQL 类型及其适用场景。
| 类型 | 适用场景 | 说明 |
|---|---|---|
| TEXT / VARCHAR | Strings, mixed content | Default safe choice; works in every SQL dialect |
| INTEGER / INT | Whole numbers (age, count, ID) | Use when column contains only digits with no decimals |
| REAL / FLOAT | Decimal numbers (price, rate) | Needed for columns with dots like 19.99 or 3.14 |
| DATE | ISO 8601 dates (2024-01-15) | Requires consistent formatting; varies by database |
| BOOLEAN | true/false, 1/0, yes/no | MySQL uses TINYINT(1); PostgreSQL has native BOOL |
| NUMERIC / DECIMAL | Exact precision (currency) | Specify scale: DECIMAL(10,2) for money columns |
| BLOB / BYTEA | Binary data | Rarely needed in CSV imports; use for hex-encoded fields |
SQL 方言对比
不同数据库引擎的 SQL 语法存在差异。生成的 INSERT 语句使用标准 SQL,在大多数系统中均可运行,但某些特性有所不同。下表总结了导入 CSV 数据时最需关注的差异点。
| 特性 | PostgreSQL | MySQL | SQLite | SQL Server |
|---|---|---|---|---|
| Identifier quoting | "col" | `col` | "col" | [col] |
| String escape | '' | '' or \' | '' | '' |
| INSERT syntax | INSERT INTO | INSERT INTO | INSERT INTO | INSERT INTO |
| Batch INSERT | VALUES (),()… | VALUES (),()… | VALUES (),()… | max 1000 rows |
| Auto-increment | SERIAL | AUTO_INCREMENT | AUTOINCREMENT | IDENTITY(1,1) |
| Upsert / merge | ON CONFLICT | ON DUPLICATE KEY | ON CONFLICT | MERGE |
| NULL handling | IS NULL | IS NULL / <=> | IS NULL | IS NULL |
| COPY from CSV | COPY … FROM | LOAD DATA INFILE | .import | BULK INSERT |
INSERT 与 COPY:如何选择导入方式
对于小到中等规模的数据集(不超过 10,000 行),INSERT 语句效果良好,且可跨所有 SQL 数据库移植。对于大规模导入,数据库提供完全绕过 SQL 解析器的批量加载命令。
代码示例
以下示例展示如何在不同编程语言中将 CSV 转换为 SQL INSERT 语句,每个示例均处理单引号转义和列名规范化。
// CSV → SQL INSERT statements
const csv = `name,age,city
Alice,30,Berlin
Bob,25,Tokyo`
function csvToSql(csv, table = 'data') {
const rows = csv.trim().split('\n').map(r => r.split(','))
const [headers, ...data] = rows
const cols = headers.map(h => h.trim().toLowerCase().replace(/\s+/g, '_'))
const values = data.map(row =>
' (' + row.map(v => `'${v.replace(/'/g, "''").trim()}'`).join(', ') + ')'
)
return `INSERT INTO ${table} (${cols.join(', ')}) VALUES
${values.join(',\n')};`
}
console.log(csvToSql(csv, 'users'))
// → INSERT INTO users (name, age, city) VALUES
// ('Alice', '30', 'Berlin'),
// ('Bob', '25', 'Tokyo');import csv
import io
csv_string = """name,age,city
Alice,30,Berlin
Bob,25,Tokyo"""
reader = csv.reader(io.StringIO(csv_string))
headers = [h.strip().lower().replace(' ', '_') for h in next(reader)]
table = 'users'
rows = list(reader)
# CREATE TABLE
col_defs = ', '.join(f'{h} TEXT' for h in headers)
print(f'CREATE TABLE {table} ({col_defs});')
# → CREATE TABLE users (name TEXT, age TEXT, city TEXT);
# INSERT statements
for row in rows:
vals = ', '.join(f"'{v.replace(chr(39), chr(39)*2)}'" for v in row)
print(f"INSERT INTO {table} ({', '.join(headers)}) VALUES ({vals});")
# → INSERT INTO users (name, age, city) VALUES ('Alice', '30', 'Berlin');
# → INSERT INTO users (name, age, city) VALUES ('Bob', '25', 'Tokyo');package main
import (
"encoding/csv"
"fmt"
"strings"
)
func csvToSQL(data, table string) string {
r := csv.NewReader(strings.NewReader(data))
records, _ := r.ReadAll()
if len(records) < 2 {
return ""
}
headers := records[0]
var vals []string
for _, row := range records[1:] {
escaped := make([]string, len(row))
for i, v := range row {
escaped[i] = "'" + strings.ReplaceAll(v, "'", "''") + "'"
}
vals = append(vals, " ("+strings.Join(escaped, ", ")+")")
}
return fmt.Sprintf("INSERT INTO %s (%s) VALUES\n%s;",
table, strings.Join(headers, ", "), strings.Join(vals, ",\n"))
}
func main() {
csv := "name,age,city\nAlice,30,Berlin\nBob,25,Tokyo"
fmt.Println(csvToSQL(csv, "users"))
// → INSERT INTO users (name, age, city) VALUES
// ('Alice', '30', 'Berlin'),
// ('Bob', '25', 'Tokyo');
}# SQLite: import CSV directly into a table sqlite3 mydb.db <<EOF .mode csv .import data.csv users SELECT * FROM users LIMIT 5; EOF # PostgreSQL: COPY from CSV file (server-side) psql -c "COPY users FROM '/path/to/data.csv' WITH (FORMAT csv, HEADER true);" # PostgreSQL: \copy from CSV (client-side, no superuser needed) psql -c "\copy users FROM 'data.csv' WITH (FORMAT csv, HEADER true);" # MySQL: LOAD DATA from CSV mysql -e "LOAD DATA INFILE '/path/to/data.csv' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS;"