여러 데이터베이스 객체 업데이트

하나 이상의 열에 대한 새로운 값을 사용하여 여러 데이터베이스 객체를 업데이트할 수 있습니다. 하나의 방법은 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로 드롭다운하는 것은 불가능합니다. 하지만, 우리는 이러한 종류의 업데이트를 생성하는 데 도움이 되는 추상화인 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

주의사항

이 도구는 매우 낮은 수준으로 동작하며 원시 열 값에 직접 작용합니다. 이를 구현하는 경우 다음 사항을 고려해야 합니다:

  • Enumerations 및 상태 필드를 해당 기본 표현으로 변환해야 합니다.
  • 중첩된 연관은 지원되지 않습니다.
  • 유효성 검사나 후크가 호출되지 않습니다.