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 مع الاقتباس، ويُهرِّب علامات الاقتباس المفردة داخل القيم (بمضاعفتها إلى '' وفق معيار SQL)، ويربط رؤوس الأعمدة بمعرّفات SQL صالحة، ويمكنه اختياريًا استنتاج أنواع البيانات. وبما يتجاوز التهريب الأساسي، يُوحِّد المحوّل الجيد أسماء الرؤوس (باستبدال المسافات والشُّرَط بشَرطات سفلية)، ويلفّ المخرج في كتلة معاملة (transaction block)، بحيث تتراجع العملية بالكامل عند فشل الاستيراد بدلًا من إبقاء بيانات جزئية في الجدول. دون محوّل، كتابة جمل INSERT يدويًا حتى لبضع مئات من الصفوف عملية مُرهِقة وعُرضة للأخطاء.
لماذا تستخدم محوّل CSV إلى SQL؟
كتابة جمل SQL INSERT يدويًا من بيانات جداول البيانات عملية مُضنية وتفتح الباب لأخطاء نحوية. يُؤتمت المحوّل الأجزاءَ المتكررة حتى تتفرّغ لتصميم المخطط والتحقق من صحة البيانات.
حالات استخدام 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: اختيار طريقة الاستيراد
لمجموعات البيانات الصغيرة والمتوسطة (أقل من 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;"