Come eseguire un aggiornamento raw sql con l’associazione dynamic nelle rotaie

Voglio eseguire un aggiornamento raw sql come di seguito:

update table set f1=? where f2=? and f3=? 

Questo SQL verrà eseguito da ActiveRecord::Base.connection.execute , ma non so come passare i valori dei parametri dinamici nel metodo.

Qualcuno potrebbe darmi qualche aiuto su di esso?

Non sembra che l’API di Rails esponga metodi per farlo genericamente. Potresti provare ad accedere alla connessione sottostante e ad usare i suoi metodi, ad esempio per MySQL:

 st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?") st.execute(f1, f2, f3) st.close 

Non sono sicuro che ci siano altre implicazioni a fare ciò (connessioni lasciate aperte, ecc.). Vorrei tracciare il codice Rails per un normale aggiornamento per vedere cosa sta facendo a parte la query reale.

L’utilizzo di query preparate può farti risparmiare un po ‘di tempo nel database, ma a meno che tu non stia facendo questo un milione di volte di fila, probabilmente staresti meglio sviluppando l’aggiornamento con la normale sostituzione di Ruby, ad esempio

 ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}") 

o usando ActiveRecord come dicevano i commentatori.

ActiveRecord::Base.connection ha un metodo di quote che accetta un valore stringa (e facoltativamente l’object colonna). Quindi puoi dire questo:

 ActiveRecord::Base.connection.execute(<<-EOQ) UPDATE foo SET bar = #{ActiveRecord::Base.connection.quote(baz)} EOQ 

Nota se sei in una migrazione Rails o un object ActiveRecord puoi accorciarlo per:

 connection.execute(<<-EOQ) UPDATE foo SET bar = #{connection.quote(baz)} EOQ 

AGGIORNAMENTO: come indicato da @kolen, dovresti usare exec_update . Questo gestirà il preventivo per te ed eviterà anche perdite di memoria. La firma funziona in modo leggermente diverso però:

 connection.exec_update(<<-EOQ, "SQL", [[nil, baz]]) UPDATE foo SET bar = $1 EOQ 

Qui l'ultimo parametro è una serie di tuple che rappresentano i parametri di collegamento. In ogni tupla, la prima voce è il tipo di colonna e il secondo è il valore. Puoi dare nil per il tipo di colonna e Rails di solito farà la cosa giusta però.

Esistono anche exec_query , exec_insert e exec_delete , a seconda di ciò che ti serve.

Dovresti semplicemente usare qualcosa come:

 YourModel.update_all( ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"}) ) 

Quello farebbe il trucco. L’uso del metodo di invio ActiveRecord :: Base # per invocare sanitize_sql_for_assignment rende il Ruby (almeno nella versione 1.8.7) saltare il fatto che sanitize_sql_for_assignment è in realtà un metodo protetto.

A volte sarebbe meglio usare il nome della class genitore invece del nome della tabella:

 # Refers to the current class self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp) 

Ad esempio “Base” “Persona”, sottoclassi (e tabelle del database) “Cliente” e “Venditore” utilizzano invece:

 Client.where(self.class.primary_key => id).update_all(created _at: timestamp) Seller.where(self.class.primary_key => id).update_all(created _at: timestamp) 

Puoi utilizzare l’object della class base in questo modo:

 person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp) 

Avevo bisogno di usare raw sql perché non ero riuscito a far funzionare composite_primary_keys con activerecord 2.3.8. Quindi, per poter accedere alla tabella sqlserver 2000 con una chiave primaria composta, era necessario il raw sql.

 sql = "update [db].[dbo].[#{Contacts.table_name}] " + "set [COLUMN] = 0 " + "where [CLIENT_ID] = '#{contact.CLIENT_ID}' and CONTACT_ID = '#{contact.CONTACT_ID}'" st = ActiveRecord::Base.connection.raw_connection.prepare(sql) st.execute 

Se è disponibile una soluzione migliore, si prega di condividere.

In Rails 3.1, è necessario utilizzare l’interfaccia di query:

  • nuovi (attributi)
  • Create (attributi)
  • creare! (attributi)
  • trovare (id_or_array)
  • distruggere (id_or_array)
  • destroy_all
  • delete (id_or_array)
  • cancella tutto
  • aggiornamento (id, aggiornamenti)
  • update_all (aggiornamenti)
  • esiste?

aggiornamento e update_all sono l’operazione che ti serve.

Vedi i dettagli qui: http://m.onkey.org/active-record-query-interface