CSV به SQL
تولید دستورات SQL INSERT از دادههای CSV
ورودی CSV
خروجی SQL
تبدیل CSV به SQL چیست؟
تبدیل CSV به SQL، مقادیر جداشده با کاما را به دستورات زبان کوئری ساختاریافته (SQL) تبدیل میکند که یک پایگاه داده رابطهای میتواند اجرا کند. رایجترین خروجی یک جفت دستور است: یک دستور CREATE TABLE که ستونها را تعریف میکند، و یک یا چند دستور INSERT INTO که آن ستونها را با سطرهای فایل CSV پر میکنند.
فایلهای CSV از مشخصات RFC 4180 پیروی میکنند. هر خط یک رکورد است، فیلدها با یک جداکننده (معمولاً کاما) از هم جدا میشوند، و فیلدهایی که شامل جداکننده یا سطر جدید هستند در گیومه دوتایی محصور میشوند. SQL، از سوی دیگر، زبان استاندارد برای مدیریت داده در سیستمهایی مانند PostgreSQL، MySQL، SQLite و SQL Server است. تبدیل بین این دو فرمت یکی از متداولترین وظایف وارد کردن داده در توسعه نرمافزار است.
یک مبدل صحیح CSV به SQL از نقلقولگذاری پشتیبانی میکند، نقلقولهای تکی داخل مقادیر را escape میکند (با دوبرابر کردن آنها به '' طبق استاندارد SQL)، سرصفحههای ستون را به شناسههای معتبر SQL تبدیل میکند، و میتواند بهصورت اختیاری نوع داده را استنتاج کند. فراتر از escape کردن پایه، یک مبدل مناسب نام سرصفحهها را نرمالسازی میکند (جایگزینی فاصله و خطتیره با زیرخط) و خروجی را در یک بلوک تراکنش محصور میکند، تا در صورت شکست وارد کردن داده، عملیات بهجای رها کردن دادههای ناقص در جدول، بهطور کامل برگشت داده شود. بدون چنین ابزاری، نوشتن دستی دستورات 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
نحو 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: انتخاب روش وارد کردن داده
برای مجموعهدادههای کوچک تا متوسط (زیر ۱۰٬۰۰۰ سطر)، دستورات INSERT بهخوبی کار میکنند و با همه پایگاههای داده SQL سازگار هستند. برای وارد کردن داده انبوه، پایگاههای داده دستورات بارگذاری انبوه فراهم میکنند که کاملاً از تجزیه SQL صرفنظر میکنند.
نمونههای کد
این نمونهها نحوه تبدیل CSV به دستورات SQL INSERT در زبانهای مختلف را نشان میدهند. هر نمونه escape کردن نقلقول تکی و پاکسازی نام ستون را پوشش میدهد.
// 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;"