아래와 같이 하나의 업데이트 원시 SQL을 실행하고 싶습니다.
update table set f1=? where f2=? and f3=?
이 SQL은에 의해 실행 ActiveRecord::Base.connection.execute
되지만 동적 매개 변수 값을 메서드에 전달하는 방법을 모르겠습니다.
누군가 내게 도움을 줄 수 있습니까?
아래와 같이 하나의 업데이트 원시 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 코드를 추적하여 수행하는 작업을 확인합니다.
준비된 쿼리를 사용하면 데이터베이스에서 약간의 시간을 절약 할 수 있지만이 작업을 연속으로 백만 번 수행하지 않는 한 일반적인 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
여기서 마지막 매개 변수는 바인드 매개 변수를 나타내는 튜플의 배열입니다. 각 튜플에서 첫 번째 항목은 열 유형이고 두 번째 항목은 값입니다. nil
열 유형에 대해 줄 수 있으며 Rails는 일반적으로 올바른 작업을 수행합니다.
이 또한 exec_query
, exec_insert
그리고 exec_delete
당신이 필요에 따라.
execute
위험한 방법이며 메모리 또는 기타 리소스 누수를 일으킬 수 있습니다.
on duplicate key update
모든 곳
ON CONFLICT DO UPDATE
원하는 경우 쿼리를 만들 수 있습니다. : 비 원시 SQL의이 보석은 편리한 보이는 github.com/jesjos/active_record_upsert을
다음과 같이 사용해야합니다.
YourModel.update_all(
ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"})
)
그것은 트릭을 할 것입니다. 은 Using 액티브 :: 자료 # 보내기 인보하는 방법을 sanitize_sql_for_assignment을 루비 (적어도 1.8.7 버전)는 사실 생략하게 sanitize_sql_for_assignment이 실제로 보호 방법입니다.
언젠가는 테이블 이름 대신 부모 클래스 이름을 사용하는 것이 좋습니다.
# 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이 자체 쿼리를 바인딩하는 방식과 유사합니다.
activerecord 2.3.8과 함께 작동하도록 composite_primary_keys를 가져 오지 못했기 때문에 원시 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은 필요한 작업입니다.
자세한 내용은 http://m.onkey.org/active-record-query-interface를 참조하십시오.