以下のような1つの更新生SQLを実行したいと思います。
update table set f1=? where f2=? and f3=?
このSQLはによって実行されますがActiveRecord::Base.connection.execute
、動的パラメーター値をメソッドに渡す方法がわかりません。
誰かが私にそれについて何か助けてもらえますか?
以下のような1つの更新生SQLを実行したいと思います。
update table set f1=? where f2=? and f3=?
このSQLはによって実行されますがActiveRecord::Base.connection.execute
、動的パラメーター値をメソッドに渡す方法がわかりません。
誰かが私にそれについて何か助けてもらえますか?
回答:
Rails APIがこれを一般的に行うメソッドを公開しているようには見えません。基礎となる接続にアクセスし、そのメソッドを使用してみることができます(例:MySQLの場合)。
st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?")
st.execute(f1, f2, f3)
st.close
これを行うことに他の影響があるかどうかはわかりません(接続を開いたままにするなど)。通常の更新のRailsコードをトレースして、実際のクエリ以外に何が行われているかを確認します。
準備されたクエリを使用すると、データベースの時間を少し節約できますが、これを100万回続けて行わない限り、通常のRuby置換を使用して更新をビルドするだけのほうがよいでしょう。
ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")
または、コメンターが言ったようにActiveRecordを使用します。
field=#{value}
SQLインジェクション攻撃の可能性が広がるため、推奨される「通常のRuby置換」方法に注意してください。そのパスをたどった場合は、ActiveRecord :: ConnectionAdapters :: Quotingモジュールを確認してください。
prepare
Mysql2で声明ません
execute
危険な方法であり、メモリやその他のリソースリークを引き起こす可能性があります。
ActiveRecord::Base.connection
有するquote
文字列値(および必要に応じて列オブジェクト)を取り方法。だからあなたはこれを言うことができます:
ActiveRecord::Base.connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{ActiveRecord::Base.connection.quote(baz)}
EOQ
Rails移行またはActiveRecordオブジェクトを使用している場合は、次のように短縮できます。
connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{connection.quote(baz)}
EOQ
更新: @kolenが指摘するように、exec_update
代わりに使用する必要があります。これにより、クォートが処理され、メモリのリークも回避されます。ただし、署名の動作は少し異なります。
connection.exec_update(<<-EOQ, "SQL", [[nil, baz]])
UPDATE foo
SET bar = $1
EOQ
ここで最後のパラメータは、バインドパラメータを表すタプルの配列です。各タプルでは、最初のエントリは列のタイプで、2番目のエントリは値です。あなたnil
は列のタイプを指定することができ、Railsは通常正しいことをします。
またexec_query
、必要に応じて、、、exec_insert
およびexec_delete
があります。
execute
危険な方法であり、メモリやその他のリソースリークを引き起こす可能性があります。
on duplicate key update
どこにでも何も残さない場合
ON CONFLICT DO UPDATE
ものなので、必要に応じてクエリを作成できます。非raw SQLの場合、このgemは便利に見えます:github.com/jesjos/active_record_upsert
テーブルの名前ではなく、親クラスの名前を使用するほうがよい場合もあります。
# Refers to the current class
self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
たとえば、「Person」基本クラス、サブクラス(およびデータベーステーブル)「Client」および「Seller」の代わりに以下を使用します。
Client.where(self.class.primary_key => id).update_all(created _at: timestamp)
Seller.where(self.class.primary_key => id).update_all(created _at: timestamp)
この方法で基本クラスのオブジェクトを使用できます:
person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
なぜこれに生のSQLを使うのですか?
あなたがそれのためのモデルを持っているなら、使用してwhere
ください:
f1 = 'foo'
f2 = 'bar'
f3 = 'buzz'
YourModel.where('f1 = ? and f2 = ?', f1, f2).each do |ym|
# or where(f1: f1, f2: f2).each do (...)
ym.update(f3: f3)
end
そのためのモデルがない場合(テーブルのみ)、継承するファイルとモデルを作成できますActiveRecord::Base
class YourTable < ActiveRecord::Base
self.table_name = 'your_table' # specify explicitly if needed
end
再びwhere
上記と同じを使用します:
ここに私が最近バインドを使って生のSQLを実行するために考え出したトリックがあります:
binds = SomeRecord.bind(a_string_field: value1, a_date_field: value2) +
SomeOtherRecord.bind(a_numeric_field: value3)
SomeRecord.connection.exec_query <<~SQL, nil, binds
SELECT *
FROM some_records
JOIN some_other_records ON some_other_records.record_id = some_records.id
WHERE some_records.a_string_field = $1
AND some_records.a_date_field < $2
AND some_other_records.a_numeric_field > $3
SQL
ここでApplicationRecord
これを定義します:
# Convenient way of building custom sql binds
def self.bind(column_values)
column_values.map do |column_name, value|
[column_for_attribute(column_name), value]
end
end
これは、ARが独自のクエリをバインドする方法に似ています。
composite_primary_keysをactiverecord 2.3.8で機能させるのに失敗したため、生のSQLを使用する必要がありました。したがって、複合主キーを使用してsqlserver 2000テーブルにアクセスするには、生の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
より良い解決策がある場合は、共有してください。
Rails 3.1では、クエリインターフェイスを使用する必要があります。
updateおよびupdate_allは、必要な操作です。