Jak przenieść kolumnę (z zawartością) do innej tabeli podczas migracji Rails?

Muszę przenieść kilka kolumn z jednej istniejącej tabeli do drugiej. Jak to zrobić za pomocą migracji rails?

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    remove_column :profiles, :someprop
  end

  def self.down
    add_column :profiles, :someprop, :string
    remove_column :users, :someprop
  end
end

Powyższe tworzy tylko nowe kolumny, ale wartości pozostają puste...

Chcę uniknąć logowania się do bazy danych, aby ręcznie aktualizować tabele.

Jeśli istnieje sposób programowego przesuwania wartości kolumn, jakie są charakterystyki wydajności? Czy zrobiłby to wiersz po wierszu, czy jest sposób na hurtową aktualizację?

Author: Eero, 2011-05-26

7 answers

W końcu skorzystałem z tej migracji (przetestowany, działa i z powodzeniem powraca):

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    execute "UPDATE users u, profiles p SET u.someprop = p.someprop WHERE u.id = p.user_id"
    remove_column :profiles, :someprop
  end

  def self.down
    add_column :profiles, :someprop, :string
    execute "UPDATE profiles p, users u SET p.someprop = u.someprop WHERE p.user_id = u.id"
    remove_column :users, :someprop
  end
end

Podoba mi się, ponieważ unika aktualizacji wiersz po wierszu w dużej bazie danych.

 35
Author: Eero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-26 09:04:57

Zrobiłbym to jako trzy migracje, lub trzyczęściowa migracja. Pierwsza część to dodawanie kolumny, druga część to kopiowanie danych, a trzecia część to opuszczanie kolumny.

Wygląda na to, że środkowy krok jest tym, o co prosisz, możesz to zrobić w Rubim poprzez zapętlenie wszystkich użytkowników i ustawienie właściwości, tak:

Users.each do |user|
   user.someprop = user.profile.some_prop
   user.save
end 
Nie podoba mi się ten sposób robienia tego, ponieważ jest to bardzo powolne. Sugerowałbym wykonanie surowego sql ' a w ten sposób:
execute "UPDATE users u, profiles p SET u.someprop=p.someprop WHERE u.id=p.user_id"

Te oba zakładają coś o Profilu / Stowarzyszenie użytkowników, które można dostosować, jeśli założyłem źle.

 6
Author: andrewmitchell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-26 08:25:05

Możesz uniknąć zakodowanych, specyficznych dla bazy danych poleceń sql za pomocą update_all i/lub find_each

 2
Author: Matt Scilipoti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-13 16:10:29

Składnia nie działa w późniejszych wersjach Postgres. Aby uzyskać zaktualizowaną odpowiedź @Eero dla postów 9.4.5 wykonaj następujące czynności:

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    execute "UPDATE users u SET someprop = (SELECT p.someprop FROM profiles p WHERE u.id = p.user_id);"
    remove_column :profiles, :someprop
  end

  def self.down
    add_column :profiles, :someprop, :string
    execute "UPDATE profiles p SET someprop = (SELECT u.someprop FROM users u WHERE p.user_id = u.id);"
    remove_column :users, :someprop
  end
end
 2
Author: pthamm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 16:34:28

Następująca składnia UPDATE działa dla ostatnich wersji Postgres i unika subquery:

class MoveSomePropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :some_property, :string
    execute "UPDATE users u SET some_property = p.some_property FROM profiles p WHERE u.id = p.user_id;"
    remove_column :profiles, :some_property
  end

  def self.down
    add_column :profiles, :some_property, :string
    execute "UPDATE profiles p SET some_property = u.some_property FROM users u WHERE p.user_id = u.id;"
    remove_column :users, :some_property
  end
end
 2
Author: Ollie Bennett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-05 12:14:41

To właśnie zrobiłem w moim projekcie: -

class MoveColumnDataToUsersTable < ActiveRecord::Migration[5.1]
  def up
    add_column :users, :someprop, :string
    User.find_each do |u|
        Profile.create!(user_id: u.id, someprop: someprop)
    end
    remove_column :profiles, :someprop
  end

  def down
    add_column :profiles, :someprop, :someprop_data_type
    Profile.find_each do |p|
      User.find_by(id: p.user_id).update_columns(someprop: p.someprop)   
    end
    Profile.destroy_all
  end
end
 0
Author: Umesh Malhotra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 17:26:41

Dla mnie (postgreSQL 9.1) surowy SQL nie działał. Zmieniłem:

" UPDATE users u
  SET someprop = (SELECT p.someprop
                  FROM profiles p
                  WHERE u.id = p.user_id );"
 -1
Author: Jeff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 16:17:00