CSV to SQL
Генерація SQL INSERT statements із CSV-даних
CSV-введення
SQL-виведення
Що таке конвертація CSV у SQL?
Конвертація CSV у SQL перетворює значення, розділені комами, на оператори мови структурованих запитів, які може виконати реляційна база даних. Найпоширенішим результатом є пара: оператор CREATE TABLE, що визначає стовпці, та один або кілька операторів INSERT INTO, які заповнюють ці стовпці рядками з CSV-файлу.
CSV-файли відповідають специфікації RFC 4180. Кожен рядок є записом, поля розділяються роздільником (зазвичай комою), а поля, що містять роздільник або символи нового рядка, беруться в подвійні лапки. SQL, своєю чергою, є стандартною мовою керування даними в таких системах, як PostgreSQL, MySQL, SQLite та SQL Server. Конвертація між цими двома форматами — одне з найпоширеніших завдань імпорту даних у розробці програмного забезпечення.
Належний конвертер CSV у SQL обробляє лапки, екранує одинарні лапки всередині значень (подвоюючи їх до '' відповідно до стандарту SQL), відображає заголовки стовпців у коректні SQL-ідентифікатори та може за потреби визначати типи даних. Окрім базового екранування, хороший конвертер нормалізує назви заголовків (замінюючи пробіли та дефіси на підкреслення) і загортає виведення в блок транзакції, щоб у разі помилки імпорту відбувся чистий відкат без часткових даних у таблиці. Без конвертера ручне написання INSERT statements навіть для кількох сотень рядків є схильним до помилок і займає багато часу.
Навіщо використовувати конвертер CSV у SQL?
Ручне написання SQL INSERT statements на основі даних із таблиць — виснажливе завдання, яке легко призводить до синтаксичних помилок. Конвертер автоматизує рутинні частини, щоб ви могли зосередитися на проектуванні схеми та перевірці даних.
Випадки використання 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 statements використовують стандартний 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 statements працюють добре та є переносними між будь-якими SQL-базами даних. Для великих імпортів бази даних надають команди масового завантаження, які повністю обходять SQL-парсер.
Приклади коду
Ці приклади показують, як конвертувати CSV у SQL INSERT statements різними мовами програмування. Кожен обробляє екранування одинарних лапок та очищення назв стовпців.
// 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;"