【问题标题】:Add NOT NULL field without default value into a populated DB将没有默认值的 NOT NULL 字段添加到填充的数据库中
【发布时间】:2014-03-31 03:08:51
【问题描述】:

我有一张桌子,我们称之为MyTable。它是 Postgresql 数据库的一部分。

MyTable 中有很多条目,比方说超过一百万。我想在这个表中添加一个字段,我们称之为MyNewField。它将由 ActiveRecord 迁移添加。

该字段没有默认值并且不能为空。结果,在它的迁移类中会是这样的:

class AddMyFieldToMyTable < ActiveRecord::Migration
  def change
    add_column :my_table, :my_field, :text, null: false
  end
end

但是,它会触发错误 (PG::NotNullViolation),因为表已经包含行,所有行都将 MyField 设置为 NULL。

我想要做的是:添加没有默认值且可为空设置为 false 的行(不触发 PG::NotNullViolation)。然后,将另一个表中的值插入到每条记录中。

这可能可以通过添加可空设置为 true 的字段,然后添加值,然后将可空设置更改回 false 来实现。不过,我很想知道是否可以一次性完成。

【问题讨论】:

    标签: ruby-on-rails ruby rails-activerecord


    【解决方案1】:

    您必须确保另一个表具有 my_table 中每个条目的 my_field 值。

    class AddMyFieldToMyTable < ActiveRecord::Migration
      def up
        add_column :my_table, :my_field, :text
        execute("insert into my_table(my_field) values (select my_field from different_table where my_table.id = different_table.different_id)")
        change_column :my_table, :my_field, :text, null: false
      end
    
      def down
        remove_column :my_table, :my_field
      end
    end
    

    【讨论】:

      【解决方案2】:

      所有迹象似乎都表明,这不可能一口气完成;您必须添加没有空约束的列,填充数据,然后添加空约束。

      class AddMyFieldToMyTable < ActiveRecord::Migration
        def change
          add_column :my_table, :my_field, :text
          reversible do |dir|
            dir.up do
              # populate my_field col
              change_column :my_table, :my_field, :text, null: false
            end
          end
        end
      end
      

      资源:


      如果您真的只想设置一次列,也许您可​​以使用临时默认值生成它,然后立即使用真实数据更新它。

      class AddMyFieldToMyTable < ActiveRecord::Migration
        def change
          add_column :my_table, :my_field, :text, default: 'tmp', null: false
          reversible do |dir|
            dir.up do
              # populate my_field col
            end
          end
        end
      end
      

      【讨论】:

        【解决方案3】:

        就我而言,我需要以两种方式偏离@usha 的答案:1) 我需要UPDATE 而不是INSERT INTO,因为我正在更新现有行的NULL 值而不是创建新行。 2) 我需要使用change_column_null 而不是change_column ... null: false。下面是一个程式化的例子。

        class AddMyFieldToMyTable < ActiveRecord::Migration
          def up
            add_column :my_table, :my_field, :text
            execute <<-SQL
              WITH values AS (
                SELECT
                  my_table.id AS my_table_id,
                  different_table.my_value
                FROM my_table
                JOIN different_table ON
                  my_table.id = different_table.different_id
              )
              UPDATE my_table
              SET my_field = values.my_value
              FROM values
              WHERE my_table.id = values.my_table_id
            SQL
            change_column_null :my_table, :my_field, false
          end
        
          def down
            remove_column :my_table, :my_field
          end
        end
        

        【讨论】:

          猜你喜欢
          • 2018-11-30
          • 2013-01-19
          • 2018-10-22
          • 2012-10-28
          • 2011-03-11
          • 1970-01-01
          • 2018-01-25
          • 2014-11-19
          • 2019-08-29
          相关资源
          最近更新 更多