Cómo ejecutar una actualización raw sql con enlace dynamic en Rails

Quiero ejecutar una actualización raw sql como a continuación:

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

SQL ejecutará este SQL mediante ActiveRecord::Base.connection.execute , pero no sé cómo pasar los valores del parámetro dynamic al método.

¿Podría alguien ayudarme con eso?

No parece que la API de Rails exponga los métodos para hacer esto genéricamente. Podría intentar acceder a la conexión subyacente y usar sus métodos, por ejemplo, para MySQL:

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

No estoy seguro si hay otras ramificaciones para hacer esto (conexiones dejadas abiertas, etc.). Me gustaría rastrear el código de Rails para una actualización normal para ver lo que está haciendo aparte de la consulta real.

El uso de consultas preparadas puede ahorrarle una pequeña cantidad de tiempo en la base de datos, pero a menos que esté haciendo esto un millón de veces seguidas, probablemente sería mejor que simplemente cree la actualización con la sustitución normal de Ruby, por ej.

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

o usando ActiveRecord como dijeron los comentaristas.

ActiveRecord::Base.connection tiene un método de quote que toma un valor de cadena (y opcionalmente el objeto de columna). Entonces puedes decir esto:

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

Tenga en cuenta que si está en una migración de Rails o un objeto ActiveRecord puede acortarlo a:

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

ACTUALIZACIÓN: Como señala @kolen, debe usar exec_update en exec_update lugar. Esto manejará las citas por usted y también evitará la pérdida de memoria. La firma funciona de manera un poco diferente:

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

Aquí el último parámetro es una matriz de tuplas que representan parámetros de enlace. En cada tupla, la primera entrada es el tipo de columna y el segundo es el valor. Puede dar nil para el tipo de columna y Rails generalmente hará lo correcto.

También hay exec_query , exec_insert y exec_delete , dependiendo de lo que necesite.

Deberías usar algo como:

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

Eso serviría. El uso del método de envío ActiveRecord :: Base # para invocar el sanitize_sql_for_assignment hace que el Ruby (al menos la versión 1.8.7) omita el hecho de que el sanitize_sql_for_assignment es en realidad un método protegido.

En algún momento sería mejor utilizar el nombre de la clase principal en lugar del nombre de la tabla:

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

Por ejemplo, clase base “Persona”, subclases (y tablas de bases de datos) “Cliente” y “Vendedor” En lugar de usar:

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

Puede usar el objeto de la clase base de esta manera:

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

Necesitaba usar sql sin procesar porque no logré que Composite_primary_keys funcione con activerecord 2.3.8. Entonces, para acceder a la tabla sqlserver 2000 con una clave primaria compuesta, se requería sql en bruto.

 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 

Si hay una mejor solución disponible, por favor comparta.

En Rails 3.1, debe usar la interfaz de consulta:

  • nuevo (atributos)
  • crear (atributos)
  • crear! (atributos)
  • find (id_or_array)
  • destroy (id_or_array)
  • destruye todo
  • eliminar (id_or_array)
  • eliminar todos
  • actualización (ids, actualizaciones)
  • update_all (actualizaciones)
  • existe?

update y update_all son la operación que necesitas.

Ver detalles aquí: http://m.onkey.org/active-record-query-interface