แปลง CSV เป็น SQL
สร้างคำสั่ง SQL INSERT จากข้อมูล CSV
CSV อินพุต
SQL เอาต์พุต
การแปลง CSV เป็น SQL คืออะไร?
การแปลง CSV เป็น SQL คือการเปลี่ยนค่าที่คั่นด้วยเครื่องหมายจุลภาคให้กลายเป็นคำสั่ง Structured Query Language ที่ฐานข้อมูลเชิงสัมพันธ์สามารถประมวลผลได้ เอาต์พุตที่พบบ่อยที่สุดคือคู่คำสั่ง: คำสั่ง CREATE TABLE ที่กำหนดคอลัมน์ และคำสั่ง INSERT INTO หนึ่งคำสั่งหรือมากกว่าที่นำข้อมูลจากแถวใน CSV ลงสู่คอลัมน์เหล่านั้น
ไฟล์ CSV ปฏิบัติตามข้อกำหนด RFC 4180 แต่ละบรรทัดคือระเบียนข้อมูลหนึ่งแถว ฟิลด์แยกจากกันด้วยตัวคั่น (โดยทั่วไปคือเครื่องหมายจุลภาค) และฟิลด์ที่มีตัวคั่นหรือบรรทัดใหม่อยู่ภายในจะถูกครอบด้วยเครื่องหมายคำพูดคู่ SQL ในทางตรงข้ามคือภาษามาตรฐานสำหรับจัดการข้อมูลในระบบอย่าง PostgreSQL, MySQL, SQLite และ SQL Server การแปลงระหว่างสองรูปแบบนี้ถือเป็นงานนำเข้าข้อมูลที่พบบ่อยที่สุดในการพัฒนาซอฟต์แวร์
ตัวแปลง CSV เป็น SQL ที่ดีจะจัดการการใช้เครื่องหมายคำพูด escape เครื่องหมาย single quote ในค่าต่างๆ (โดยซ้ำเป็น '' ตามมาตรฐาน SQL) แมปหัวคอลัมน์ให้เป็น SQL identifier ที่ถูกต้อง และยังสามารถอนุมานประเภทข้อมูลได้ด้วย นอกจากการ escape พื้นฐาน ตัวแปลงที่ดีจะจัดรูปแบบชื่อคอลัมน์ (แทนที่ช่องว่างและขีดกลางด้วยขีดล่าง) และห่อเอาต์พุตไว้ใน transaction block เพื่อให้การนำเข้าที่ล้มเหลว rollback กลับได้อย่างสะอาด แทนที่จะทิ้งข้อมูลที่นำเข้าเพียงบางส่วนไว้ในตาราง หากไม่มีตัวแปลง การเขียนคำสั่ง INSERT ด้วยมือแม้แต่สำหรับไม่กี่ร้อยแถวก็มีโอกาสผิดพลาดสูงและเสียเวลามาก
ทำไมต้องใช้ตัวแปลง CSV เป็น SQL?
การเขียนคำสั่ง SQL INSERT ด้วยมือจากข้อมูลสเปรดชีตเป็นงานที่น่าเบื่อและเสี่ยงต่อข้อผิดพลาดด้านไวยากรณ์ ตัวแปลงจะทำส่วนที่ซ้ำซ้อนให้อัตโนมัติ เพื่อให้คุณมุ่งเน้นที่การออกแบบ schema และการตรวจสอบข้อมูลได้
กรณีการใช้งาน CSV เป็น SQL
เอกสารอ้างอิงประเภทข้อมูล SQL
เมื่อแปลง CSV เป็น SQL ทุกคอลัมน์จะใช้ค่าเริ่มต้นเป็น TEXT เพราะ CSV ไม่มีข้อมูลระบุประเภท หากคุณทราบประเภทของคอลัมน์ คุณสามารถแทนที่ TEXT ในเอาต์พุต CREATE TABLE ได้ ตารางนี้แสดงประเภท 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 Dialect
ไวยากรณ์ SQL มีความแตกต่างระหว่าง database engine ต่างๆ คำสั่ง 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 vs COPY: การเลือกวิธีนำเข้าข้อมูล
สำหรับชุดข้อมูลขนาดเล็กถึงกลาง (ต่ำกว่า 10,000 แถว) คำสั่ง INSERT ทำงานได้ดีและใช้งานร่วมกันได้กับ SQL database ทุกชนิด สำหรับการนำเข้าขนาดใหญ่ ฐานข้อมูลมีคำสั่งโหลดข้อมูลจำนวนมากที่ข้ามตัวแยกวิเคราะห์ SQL ได้โดยตรง
ตัวอย่างโค้ด
ตัวอย่างเหล่านี้แสดงวิธีแปลง CSV เป็นคำสั่ง SQL INSERT ในภาษาโปรแกรมต่างๆ แต่ละตัวอย่างจัดการการ escape เครื่องหมาย single quote และการทำให้ชื่อคอลัมน์เป็น identifier ที่ถูกต้อง
// 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;"