Rails 마이그레이션에서 한 열을 다른 열의 값으로 업데이트


80

저는 Rails 앱에 수십만 개의 레코드가있는 테이블이 있으며 created_at타임 스탬프 만 있습니다 . 이 레코드를 편집하는 기능을 추가하고 있으므로 updated_at테이블에 타임 스탬프를 추가하려고 합니다. 열을 추가하기 위해 마이그레이션 할 때 Rails에서 새로 생성 된 행의 기본값이기 때문에 모든 행을 업데이트하여 새 행 updated_at과 이전을 일치시키고 싶습니다 created_at. a를 수행 find(:all)하고 레코드를 반복 할 수 있지만 테이블 크기 때문에 몇 시간이 걸립니다. 내가 정말로하고 싶은 것은 :

UPDATE table_name SET updated_at = created_at;

원시 SQL을 실행하는 대신 ActiveRecord를 사용하여 Rails 마이그레이션을 수행하는 더 좋은 방법이 있습니까?

답변:


136

마이그레이션을 만들 것입니다.

rails g migration set_updated_at_values

그리고 그 안에 다음과 같이 작성하십시오.

class SetUpdatedAt < ActiveRecord::Migration
  def self.up
    Yourmodel.update_all("updated_at=created_at")
  end

  def self.down
  end
end

이렇게하면 두 가지를 달성 할 수 있습니다.

  • 이는 가능한 각 배포 (필요한 경우)가 실행되는 반복 가능한 프로세스입니다.
  • 이것은 효율적입니다. 나는 더 많은 루비 스크 솔루션을 생각할 수 없습니다 (효율적입니다).

참고 : activerecord를 사용하여 쿼리를 작성하기가 너무 어려워지면 마이그레이션 내에서 원시 SQL을 실행할 수도 있습니다. 다음을 작성하십시오.

Yourmodel.connection.execute("update your_models set ... <complicated query> ...")

+1-최근에 정확히이 작업을 수행하고 ActiveRecord를 통해 SQL을 사용했습니다. 가능한 한 빠릅니다.
Peter Brown

40
Yourmodel.update_all 'update_at=created_at'더 좋죠? 범위에서도 작동합니다.
Marc-André Lafortune 2013 년

Rails 가이드 에 따르면 : "데이터베이스 스키마는 up다음 을 수행하면 변경되지 않아야합니다 down" . 따라서 def change대신 고려하십시오 .
EliadL

1
@EliadL 몇 가지 언급 : 1) 스키마를 변경하지 않고 데이터베이스의 내용 만 변경합니다. 그리고이 답변이 작성된 시점에서 2)의 change방법은 아직 존재하지 않았지만,이 경우에 나는 아직도 명시 적으로 사용하는 것을 선호 up하고 down당신이 무엇을 제어하고 싶을 경우 (더 명시 적으로 down)해야한다.
nathanvda

20

raw SQL과 매우 유사하게 작동 하는 update_all 을 사용할 수 있습니다 . 그것이 당신이 가진 모든 옵션입니다.

BTW 개인적으로 나는 마이그레이션에 그다지 관심을 기울이지 않습니다. 때로는 원시 SQL이 정말 최상의 솔루션입니다. 일반적으로 마이그레이션 코드는 재사용되지 않습니다. 이것은 일회성 작업이므로 코드 순도에 대해서는 신경 쓰지 않습니다.


2
이는 배포 요구 사항에 따라 다릅니다. 마이그레이션은 기존 플랫폼에 반복적으로 배포하고 동일한 결과를 얻을 수 있기 때문에 마이그레이션을 사용하는 것을 정말 좋아합니다. 배포 단계에는 개발, 테스트, qa / acceptance, 개념 증명 플랫폼 (테스트 클라이언트 용), 프로덕션 플랫폼이 있습니다. 기존 데이터를 결함없이 새로 배포 된 버전으로 마이그레이션 할 수 있어야합니다. 열을 추가하고 데이터가 정상인지 확인하는 것은 일회성 작업이 아닙니다.
nathanvda 2011 년

update_all마이그레이션 파일 내부 사용에 대해 씁니다. :-) 마이그레이션 파일 내부에서 원시 SQL을 실행할 수도 있습니다. 그러나 update_all조금 더 우아합니다. 둘 다 정확히 동일하게 수행됩니다.
Greg Dan

원래 모델이 나중에 재정의되는 경우 문제를 방지 할 수 있기 때문에 일반적으로 마이그레이션에서 모델을 선언하는 것이 현명한 생각입니다. : 그냥 아주 능숙하게 모든 것을 설명이 문서 발견 complicated-simplicity.com/2010/05/...
프랑수아 보 솔레이

함께 update_all내가 어떻게 영업 이익은 요청에 따라, 또 다른의에 열의 값을 설정하는 방법 아무 생각이 없습니다. 보여주세요.
nathanvda 2011 년

14

gregdan이 썼 듯이 update_all. 다음과 같이 할 수 있습니다.

Model.where(...).update_all('updated_at = created_at')

첫 번째 부분은 일반적인 조건 세트입니다. 마지막 부분은 할당 방법을 설명합니다. 이것은 UPDATE최소한 Rails 4에서 성명서 를 생성합니다 .


이것은 4.2에서 생성 SET'posts'.'email' = 'options'되며, 옵션은 리터럴 문자열입니다
lulalala

이 팁을 확인하는 것이 저에게도 효과가 없습니다. 완전히 잘못된 솔루션인지 확실하지 않습니다. donwvote @martin 대답을하지 마십시오
woto oto

Rails 콘솔의 출력은 다음과 같습니다.User.update_all('updated_at = created_at') SQL (0.4ms) UPDATE "users" SET updated_at = created_at
Martin Streicher 2015 년

2

다음 명령을 직접 실행할 수 있습니다. rails console ActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")

예 : 항목 테이블의 remote_id로 항목 테이블의 sku를 업데이트하고 싶습니다. 명령은 다음과 같습니다.
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")


실제로 이것은 가장 안전한 방법입니다. 왜냐하면 미래에 (일부는 마이그레이션을 실행할 때 모델 Yourmodel이 이미 삭제 될 수 있기 때문입니다. 마이그레이션에서 모델을 사용하지 않도록하십시오.)
Foton

0

이것은 질의가 위험에 노출되기 때문에 질의를 작성할 필요없이 일반적인 해결 방법입니다.

  class Demo < ActiveRecord::Migration
    def change
     add_column :events, :time_zone, :string
     Test.all.each do |p|
       p.update_attributes(time_zone: p.check.last.time_zone)
     end
     remove_column :sessions, :time_zone
    end
  end

-4

일회성 작업으로 rails console. 정말 몇 시간이 걸릴까요? 수백만 개의 레코드가 있다면 ...

records = ModelName.all; records do |r|; r.update_attributes(:updated_at => r.created_at); r.save!; end;`

그것은 본질적으로 내가 먼저 시도한 것이지만 변경해야 할 레코드가 수십만 개이므로 몇 시간 (일?)이 걸립니다.
jrdioko 2011 년

테스트했을 때 내 개발자 컴퓨터 (서버가 아님)에서 초당 약 50 개의 레코드를 기록했습니다.
jrdioko 2011 년

4
가능하면 항상 반복을 피하고, 모든 레코드를 한 번에 RAM에로드하는 'all'을 사용하지 마십시오. update_attributes는 이미 자동으로 저장하므로 추가 호출을 저장합니다! 전체 작업에 두 배의 시간이 소요됩니다.
ryan0 2015 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.