Frage Wie führe ich eine rohe Update-SQL mit dynamischer Bindung in Rails aus?


Ich möchte ein Update Raw SQL wie folgt ausführen:

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

Diese SQL wird von ausgeführt ActiveRecord::Base.connection.execute, aber ich weiß nicht, wie die dynamischen Parameterwerte in die Methode übernommen werden sollen.

Könnte mir jemand helfen?


75
2017-12-19 13:04


Ursprung


Antworten:


Es sieht nicht so aus, als ob die Rails-API Methoden dafür offen legt, dies generisch zu tun. Sie könnten versuchen, auf die zugrunde liegende Verbindung zuzugreifen und deren Methoden zu verwenden, z. für MySQL:

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

Ich bin mir nicht sicher, ob es andere Konsequenzen gibt (offene Verbindungen, usw.). Ich würde den Rails-Code für ein normales Update verfolgen, um zu sehen, was er neben der tatsächlichen Abfrage tut.

Durch die Verwendung vorbereiteter Abfragen können Sie eine gewisse Zeit in der Datenbank sparen. Wenn Sie dies jedoch nicht millionenfach hintereinander tun, ist es wahrscheinlich besser, das Update nur mit normaler Ruby-Substitution zu erstellen, z.

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

oder mit ActiveRecord wie die Kommentatoren sagten.


79
2017-12-19 18:54



ActiveRecord::Base.connection hat ein quote Methode, die einen String-Wert (und optional das Spaltenobjekt) übernimmt. So können Sie das sagen:

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

Wenn Sie sich in einer Rails-Migration oder einem ActiveRecord-Objekt befinden, können Sie dies folgendermaßen verkürzen:

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

AKTUALISIEREN: Wie @Kolen hervorhebt, solltest du verwenden exec_update stattdessen. Dies behandelt das Zitat für Sie und vermeiden Sie auch Speicherverlust. Die Signatur funktioniert jedoch ein bisschen anders:

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

Hier ist der letzte Parameter ein Array von Tupeln, die Bindungsparameter darstellen. In jedem Tupel ist der erste Eintrag der Spaltentyp und der zweite der Wert. Du kannst Geben nil für den Spaltentyp und Rails wird normalerweise das Richtige tun.

Es gibt auch exec_query, exec_insert, und exec_delete, je nachdem, was Sie brauchen.


20
2017-07-01 23:01



Sie sollten nur etwas wie:

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

Das würde den Trick bringen. Verwendung der ActiveRecord :: Base # senden Methode zum Aufrufen der sanitize_sql_for_assignment Der Ruby (zumindest die Version 1.8.7) überspringt die Tatsache, dass der sanitize_sql_for_assignment ist eigentlich eine geschützte Methode.


4
2018-05-15 03:12



Manchmal wäre es besser, den Namen der Elternklasse anstelle des Namens der Tabelle zu verwenden:

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

Zum Beispiel "Person" Basisklasse, Unterklassen (und Datenbanktabellen) "Client" und "Verkäufer" Stattdessen mit:

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

Sie können das Objekt der Basisklasse auf diese Weise verwenden:

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

2
2017-11-29 20:58



Ich musste raw sql verwenden, weil ich composite_primary_keys nicht mit activerecord 2.3.8 arbeiten konnte. Um auf die Tabelle sqlserver 2000 mit einem zusammengesetzten Primärschlüssel zugreifen zu können, wurde row sql benötigt.

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

Wenn eine bessere Lösung verfügbar ist, teilen Sie diese bitte mit.


-8
2018-04-25 18:06



In Rails 3.1 sollten Sie die Query-Schnittstelle verwenden:

  • neu (Attribute)
  • create (Attribute)
  • create! (Attribute)
  • Finde (id_or_array)
  • zerstören (id_or_array)
  • destroy_all
  • löschen (id_or_array)
  • alles löschen
  • Update (IDs, Updates)
  • update_all (Aktualisierungen)
  • existiert?

update und update_all sind die Operation, die Sie benötigen.

Siehe Details hier: http://m.onkey.org/active-record-query-interface


-13
2017-11-11 09:40