2012-10-03 13 views
10

Ho un modello di valutazione. La valutazione ha molti punteggi. Ogni volta che viene creata una nuova valutazione, viene creato un record di punteggio per ogni utente che ha bisogno di una valutazione (vedi sotto per il metodo corrente che sto usando per fare ciò.) Quindi, ad esempio, possono essere creati 40 record di punteggi contemporaneamente. Il proprietario della valutazione aggiorna quindi ogni record del punteggio con il punteggio dell'utente.Rails 3, istruzione di inserimento SQL raw personalizzata

Sto cercando di utilizzare SQL raw perché ogni inserto è la propria transazione ed è lento.

vorrei convertire la segue in un istruzione di inserimento massa utilizzando SQL prime:

def build_evaluation_score_items 
    self.job.active_employees.each do |employee| 
    employee_score = self.scores.build 
    employee_score.user_id = employee.id 
    employee_score.save 
    end 
end 

Ogni pensiero su come questo può essere fatto? Ho provato ad adattare un esempio di codice dal sito Coffee Powered di Chris Heald ma, niente da fare.

Grazie a chiunque sia disposto ad aiutare!

EDIT 1

ho trascurato di menzionare il metodo corrente è avvolto in una transazione.

Quindi, in sostanza, sto cercando di aggiungere questo al blocco di codice in modo che tutto sia inserito in una dichiarazione (** Questo snippit di codice proviene dal sito Coffee Powered di Chris Heald che ha discusso l'argomento. il post è> 3 anni):. di nuovo

inserts = [] 
TIMES.times do 
    inserts.push "(3.0, '2009-01-23 20:21:13', 2, 1)" 
end 
sql = "INSERT INTO user_node_scores (`score`, `updated_at`, `node_id`, `user_id`)VALUES #{inserts.join(", ")}" 

sarei felice di mostrare il codice da alcuni dei miei tentativi che non funzionano ...

Grazie!

Beh, ho messo insieme qualcosa che somiglia il codice di cui sopra, ma ottengo un errore non valido un'istruzione SQL intorno alla porzione ('evaluation_id'. Ogni pensiero?

def build_evaluation_score_items 
    inserts = [] 
    self.job.active_employees.each do |employee| 
    inserts.push "(#{self.id}, #{employee.id}, #{Time.now}, #{Time.now})" 
    end 
    sql = "INSERT INTO scores ('evaluation_id', `user_id`, 'created_at', `updated_at`)VALUES #{inserts.join(", ")}" 
    ActiveRecord::Base.connection.execute(sql) 
end 

alcuna idea di ciò che nel ? sopra SQL codice è causa l'errore

+1

parte created_at non funziona per me dal momento che è la creazione di una stringa che postgres non è l'analisi. 'ActiveRecord :: StatementInvalid: PG :: SyntaxError: ERRORE: errore di sintassi ao vicino a" 07 " LINEA 4: (1, 1, 100, 2017-02-20 07:00:56 +0000, 2017-02-20 07 ... ' – Gambo

risposta

17

Bene, dopo molte prove ed errori, ecco la risposta finale. La cosa bella è che tutti i record sono inseriti tramite una dichiarazione. Ovviamente, le convalide vengono saltate (quindi non è appropriato se si richiedono le convalide del modello durante la creazione), ma nel mio caso, ciò non è necessario perché tutto ciò che sto facendo è impostare il record del punteggio per la valutazione di ciascun dipendente. Naturalmente, le convalide funzionano come previsto quando il responsabile del lavoro aggiorna il punteggio di valutazione del dipendente.

def build_evaluation_score_items 
    inserts = [] 
    time = Time.now.to_s(:db) 
    self.job.active_employees.each do |employee| 
    inserts.push "(#{self.id}, #{employee.id}, '#{time}')" 
    end 
    sql = "INSERT INTO scores (evaluation_id, user_id, created_at) VALUES #{inserts.join(", ")}" 
    ActiveRecord::Base.connection.execute(sql) 
end 
+0

Ti ho usato r soluzione per mettere insieme qualcosa per i miei bisogni. Stavo facendo alcuni test delle prestazioni, e avevo bisogno di riempire il DB con molti dati casuali (milioni di righe) * rapidamente * (cioè non usando ActiveRecord). – user664833

+1

Questo non funziona per me perché 'time' ha un output errato. Postgres lancia questo: 'ActiveRecord :: StatementInvalid: PG :: SyntaxError: ERRORE: errore di sintassi ao vicino a" 07 " LINEA 4: (1, 1, 100, 2017-02-20 07:04:11, 2017- 02-20 07:04:11 ... ' – Gambo

+0

Utilizzare insert per cancellare la cache. – MRifat

5

Penso che quello che stai cercando è:

def build_evaluation_score_items 
    ActiveRecord::Base.transaction do 
    self.job.active_employees.each do |employee| 
     employee_score = self.scores.build 
     employee_score.user_id = employee.id 
     employee_score.save 
    end 
    end 
end 

Tutte le transazioni figlio vengono automaticamente "spinto" fino al tran genitore saction. Ciò impedirà il sovraccarico di così tante transazioni e dovrebbe aumentare le prestazioni.

È possibile leggere ulteriori informazioni sulle transazioni ActiveRecord here.

UPDATE

Scusa, ho frainteso. Mantenere la risposta sopra per i posteri. Prova questo:

def build_evaluation_score_items 
    raw_sql = "INSERT INTO your_table ('user_id', 'something_else') VALUES " 
    insert_values = "('%s', '%s')," 
    self.job.active_employees.each do |employee| 
    raw_sql += insert_values % employee.id, "something else" 
    end 
    ActiveRecord::Base.connection.execute raw_sql 
end 
+0

Grazie per il suggerimento. Ho trascurato di indicare nel mio post che attualmente la dichiarazione è racchiusa in una transazione nel codice ma che è ancora sub-ottimale, poiché esegue ancora istruzioni di inserimento per ogni dipendente. L'unico vantaggio per la transazione è che l'indice db non è aggiornato –

+0

Ho aggiornato la mia risposta con una soluzione potenziale Per favore fammi sapere se ti aiuta –

+0

Ciao Chuck - Sono d'accordo con mantenere il primo elemento lì, ecco perché Il metodo di transazione ha senso, ma sfortunatamente ricevo il seguente errore: metodo non definito 'execute 'per ActiveRecord :: Base: Class. Scavalgerò il comando di esecuzione appropriato e riferirò. –

6

Piuttosto che costruire SQL direttamente (e voi stessi aprendo a iniezione SQL e altre questioni), mi sento di raccomandare il activerecord-import gem. Può emettere comandi multi-row INSERT, tra le altre strategie.

Si potrebbe scrivere qualcosa come:

def build_evaluation_score_items 
    new_scores = job.active_employees.map do |employee| 
    scores.build(:user_id => employee.id) 
    end 
    Score.import new_scores 
end