2012-03-08 6 views
5

Sto creando uno script Ruby per importare un file di testo delimitato da tabulazioni di circa 150k righe in SQLite. Qui è così lontano:Strings Fuga per Ruby SQLite Inserire

require 'sqlite3' 

file = File.new("/Users/michael/catalog.txt") 
string = [] 

# Escape single quotes, remove newline, split on tabs, 
# wrap each item in quotes, and join with commas 
def prepare_for_insert(s) 
    s.gsub(/'/,"\\\\'").chomp.split(/\t/).map {|str| "'#{str}'"}.join(", ") 
end 

file.each_line do |line| 
    string << prepare_for_insert(line) 
end 

database = SQLite3::Database.new("/Users/michael/catalog.db") 

# Insert each string into the database 
string.each do |str| 
    database.execute("INSERT INTO CATALOG VALUES (#{str})") 
end 

Gli errori di script fuori sulla prima riga contenente un singolo citazione nonostante la gsub per sfuggire apici nel mio metodo prepare_for_insert:

/Users/michael/.rvm/gems/ruby-1.9.3-p0/gems/sqlite3-1.3.5/lib/sqlite3/database.rb:91: 
in `initialize': near "s": syntax error (SQLite3::SQLException) 

E 'erroring su riga 15. Se ispeziono quella riga con puts string[14], posso vedere dove mostra l'errore vicino a "s". Ecco come si presenta: 'Touch the Top of the World: A Blind Man\'s Journey to Climb Farther Than the Eye Can See'

Sembra che la sola offerta è sfuggito, e allora perché sto ancora ricevendo l'errore?

risposta

10

Non farlo affatto così, interpolazione stringa e SQL tendono ad essere una pessima combinazione. Utilizzare una dichiarazione preparata, invece, e lasciare che l'affare con conducente citando e fuggire:

# Ditch the gsub in prepare_for_insert and... 
db = SQLite3::Database.new('/Users/michael/catalog.db') 
ins = db.prepare('insert into catalog (column_name) values (?)') 
string.each { |s| ins.execute(s) } 

È necessario sostituire column_name con il vero nome della colonna naturalmente; non devi specificare i nomi delle colonne in un INSERT, ma dovresti sempre farlo comunque. Se è necessario inserire più colonne, aggiungere ulteriori segnaposto e argomenti a ins.execute.

Utilizzando prepare e execute dovrebbe essere più veloce, più sicuro, più facile, e non farà sentire come si sta scrivendo PHP nel 1999.

Inoltre, è necessario utilizzare il standard CSV parser per analizzare scheda-separato i file, i formati XSV non sono molto divertenti da affrontare (sono davvero malvagi di fatto) e tu hai cose migliori da fare con il tuo tempo che affrontare i loro casi privi di senso e limite e cosa no.

+0

Perfetto. Ora funziona. Ci sono 34 colonne. Quindi, buffo come sembra, '(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?,?,?,?,?,?,?,?,?) 'è il modo corretto di esprimere il numero previsto di colonne? Incorporerò anche il parser CSV. – michaelmichael

+3

@michaelmichael: Sì, e 34 argomenti per 'ins.execute' ma si può mettere/mantenere gli argomenti in un array e' ins.execute (* array) 'loro di mantenere il goofiness sotto controllo. Puoi anche costruire il segnaposto usando '(['?'] * 34) .join (',')' (questo è ok come sai esattamente quali stringhe stai lavorando, provare a usare stringhe sconosciute in SQL è solo chiedere guai però). –