여러 데이터베이스 객체 업데이트
GitLab v19.4요약
하나 이상의 칼럼에 새 값을 설정하여 여러 데이터베이스 객체를 업데이트할 수 있습니다. 업데이트를 정적 값(1)이나 계산식(2)으로 표현할 수 없다면, 단일 쿼리에서 여러 행을 각각 다른 값으로 업데이트하도록 UPDATE FROM을 사용합니다.
하나 이상의 칼럼에 새 값을 설정하여 여러 데이터베이스 객체를 업데이트할 수 있습니다.
한 가지 방법은 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을 사용합니다.
임시 테이블이나 공통 테이블 표현식(Common Table Expression, 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(..)
# Issues a single query:
::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(..)
# Issues two queries
::Gitlab::Database::BulkUpdate.execute(%i[title], {
issue_a => { title: 'A' },
issue_b => { title: 'B' },
merge_request => { title: 'B' }
})
객체가 올바른 모델 클래스를 반환하지 않는다면, 예를 들어 union의 일부라면 블록에서 모델 클래스를 명시적으로 지정합니다.
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)
])
# At this point, all the objects are instances of Foo, even the ones from the
# Bar table
mapping = objects.to_h { |obj| [obj, bazzes[obj.id]] }
# Issues at most 2 queries
::Gitlab::Database::BulkUpdate.execute(%i[baz], mapping) do |obj|
obj.object_type.constantize
end
주의 사항#
이 도구는 수준이 매우 낮아 원시 칼럼 값에 직접 작용합니다. 구현할 때는 다음 사항을 고려해야 합니다.
- 열거형과 상태 필드는 기반이 되는 표현으로 변환해야 합니다.
- 중첩 연관 관계는 지원되지 않습니다.
- 유효성 검사나 훅은 호출되지 않습니다.