CSV in SQL
Genera istruzioni SQL INSERT da dati CSV
Input CSV
Output SQL
Cos'è la conversione da CSV a SQL?
La conversione da CSV a SQL trasforma valori separati da virgola in istruzioni Structured Query Language che un database relazionale può eseguire. L'output più comune è una coppia: un'istruzione CREATE TABLE che definisce le colonne e una o più istruzioni INSERT INTO che popolano quelle colonne con le righe del file CSV.
I file CSV seguono la specifica RFC 4180. Ogni riga è un record, i campi sono separati da un delimitatore (di solito una virgola) e i campi che contengono il delimitatore o ritorni a capo sono racchiusi tra virgolette doppie. SQL, invece, è il linguaggio standard per la gestione dei dati in sistemi come PostgreSQL, MySQL, SQLite e SQL Server. La conversione tra questi due formati è una delle operazioni di importazione dati più frequenti nello sviluppo software.
Un convertitore CSV-to-SQL corretto gestisce la delimitazione, esegue l'escape delle virgolette singole nei valori (raddoppiandole in '' secondo lo standard SQL), mappa le intestazioni delle colonne in identificatori SQL validi e può facoltativamente dedurre i tipi di dato. Oltre all'escape di base, un buon convertitore normalizza i nomi delle intestazioni (sostituendo spazi e trattini con underscore) e racchiude l'output in un blocco di transazione, in modo che un errore di importazione esegua il rollback in modo pulito invece di lasciare dati parziali nella tabella. Senza un convertitore, scrivere a mano istruzioni INSERT anche per poche centinaia di righe è lento e soggetto a errori.
Perché usare un convertitore CSV in SQL?
Scrivere manualmente istruzioni SQL INSERT da dati di un foglio di calcolo è tedioso e porta facilmente a errori di sintassi. Un convertitore automatizza le parti ripetitive in modo da potersi concentrare sulla progettazione dello schema e sulla validazione dei dati.
Casi d'uso di CSV in SQL
Riferimento ai tipi di dato SQL
Quando si converte da CSV a SQL, ogni colonna viene impostata per impostazione predefinita su TEXT perché il CSV non ha metadati di tipo. Se conosci i tipi di colonna, puoi sostituire TEXT nell'output CREATE TABLE. Questa tabella mostra i tipi SQL più comuni e quando usarli.
| Tipo | Utilizzato per | Note |
|---|---|---|
| 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 |
Confronto tra dialetti SQL
La sintassi SQL varia tra i motori di database. Le istruzioni INSERT generate usano SQL standard che funziona nella maggior parte dei sistemi, ma alcune caratteristiche differiscono. Questa tabella riassume le differenze più rilevanti quando si importano dati CSV.
| Caratteristica | 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: scegliere il metodo di importazione
Per dataset di piccole e medie dimensioni (meno di 10.000 righe), le istruzioni INSERT funzionano bene e sono portabili su tutti i database SQL. Per importazioni di grandi dimensioni, i database forniscono comandi di caricamento in blocco che bypassano interamente il parser SQL.
Esempi di codice
Questi esempi mostrano come convertire CSV in istruzioni SQL INSERT in diversi linguaggi. Ognuno gestisce l'escape delle virgolette singole e la normalizzazione dei nomi delle colonne.
// 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;"