여러 데이터베이스 객체 업데이트
하나 이상의 열에 대한 새 값으로 여러 데이터베이스 객체를 업데이트할 수 있습니다.
하나의 방법은 Relation#update_all
을 사용하는 것입니다.
user.issues.open.update_all(due_date: 7.days.from_now) # (1)
user.issues.update_all('relative_position = relative_position + 1') # (2)
업데이트를 정적 값(1) 또는 계산(2)으로 표현할 수 없다면,
단일 쿼리에서 고유한 값을 가진 여러 행을 업데이트해야 할 필요를 나타내기 위해 UPDATE FROM
을 사용합니다.
임시 테이블이나 공통 테이블 표현식(CTE)을 만들고 업데이트의 소스로 사용합니다.
with updates(obj_id, new_title, new_weight) as (
values (1 :: integer, 'Very difficult issue' :: text, 8 :: integer),
(2, 'Very easy issue', 1)
)
update issues
set title = new_title, weight = new_weight
from updates
where id = obj_id
이를 ActiveRecord로 표현하거나 Arel로 변경하는 것은 불가능합니다.
그러나, UpdateManager
가 update from
을 지원하지 않기 때문에 이러한 종류의 업데이트를 생성하는 데 도움이 되는 추상화인 Gitlab::Database::BulkUpdate
를 제공합니다.
이 추상화는 바인딩 매개변수를 사용하여 SQL 인젝션을 피하고 이전 예제와 유사한 쿼리를 생성합니다.
사용법
Gitlab::Database::BulkUpdate
를 사용하려면 다음이 필요합니다:
- 업데이트할 열 목록.
- 객체(또는 ID)에서 해당 객체에 대한 새 값으로 설정하기 위한 매핑.
- 각 객체의 테이블을 결정하는 방법.
예를 들어, 다음과 같이 object.class.table_name
을 호출하여 예제 쿼리를 표현할 수 있습니다:
issue_a = Issue.find(..)
issue_b = Issue.find(..)
# 단일 쿼리를 수행합니다:
::Gitlab::Database::BulkUpdate.execute(%i[title weight], {
issue_a => { title: 'Very difficult issue', weight: 8 },
issue_b => { title: 'Very easy issue', weight: 1 }
})
업데이트가 모두 해당 객체에 대한 의미를 가진 경우, 이질적인 객체 세트도 전달할 수 있습니다:
issue_a = Issue.find(..)
issue_b = Issue.find(..)
merge_request = MergeRequest.find(..)
# 두 개의 쿼리를 수행합니다
::Gitlab::Database::BulkUpdate.execute(%i[title], {
issue_a => { title: 'A' },
issue_b => { title: 'B' },
merge_request => { title: 'B' }
})
객체가 정확한 모델 클래스를 반환하지 않는 경우(예: 이들이 묶음에 속해 있는 경우)명시적으로 모델 클래스를 지정해야 합니다:
bazzes = params
objects = Foo.from_union([
Foo.select("id, 'foo' as object_type").where(quux: true),
Bar.select("id, 'bar' as object_type").where(wibble: true)
])
# 이 시점에서 모든 객체는 Foo의 인스턴스입니다. Bar 테이블에서 비롯된 객체들도 포함됩니다
mapping = objects.to_h { |obj| [obj, bazzes[obj.id]] }
# 최대 2개의 쿼리를 수행합니다
::Gitlab::Database::BulkUpdate.execute(%i[baz], mapping) do |obj|
obj.object_type.constantize
end
주의사항
이 도구는 매우 저수준이며, 원시 열 값을 직접 처리합니다. 구현 시 이러한 문제점을 고려해야 합니다:
- 열거 및 상태 필드는 기저 표현으로 변환되어야 합니다.
- 중첩된 연관관계는 지원되지 않습니다.
- 유효성 검사 또는 후크가 호출되지 않습니다.