尝试将NOT NULL列添加到现有表时,我收到以下错误.为什么会这样?我试过rake db:reset认为现有记录是问题,但即使重置数据库后,问题仍然存在.你能帮我搞清楚吗?
迁移文件
class AddDivisionIdToProfile < ActiveRecord::Migration
def self.up
add_column :profiles, :division_id, :integer, :null => false
end
def self.down
remove_column :profiles, :division_id
end
end
错误信息
SQLite3 :: SQLException:无法添加带有默认值NULL的NOT NULL列:ALTER TABLE"profiles"ADD"division_id"integer NOT NULL
Jaime Bellmy.. 159
这是(我会考虑的)SQLite的一个小故障.无论表中是否有任何记录,都会发生此错误.
从头开始添加表时,可以指定NOT NULL,这是您使用":null => false"表示法所做的操作.但是,添加列时无法执行此操作.SQLite的规范说你必须有一个默认值,这是一个糟糕的选择.添加默认值不是一个选项,因为它违背了具有NOT NULL外键的目的 - 即数据完整性.
这是一种解决这个故障的方法,您可以在同一个迁移中完成所有操作.注意:这适用于您尚未在数据库中拥有记录的情况.
class AddDivisionIdToProfile < ActiveRecord::Migration def self.up add_column :profiles, :division_id, :integer change_column :profiles, :division_id, :integer, :null => false end def self.down remove_column :profiles, :division_id end end
我们在没有NOT NULL约束的情况下添加列,然后立即更改列以添加约束.我们可以这样做,因为虽然SQLite在列添加期间显然非常关注,但是对于列更改它并不那么挑剔.这是我书中清晰的设计气味.
它绝对是一个黑客,但它比多次迁移更短,它仍然适用于生产环境中更强大的SQL数据库.
这是(我会考虑的)SQLite的一个小故障.无论表中是否有任何记录,都会发生此错误.
从头开始添加表时,可以指定NOT NULL,这是您使用":null => false"表示法所做的操作.但是,添加列时无法执行此操作.SQLite的规范说你必须有一个默认值,这是一个糟糕的选择.添加默认值不是一个选项,因为它违背了具有NOT NULL外键的目的 - 即数据完整性.
这是一种解决这个故障的方法,您可以在同一个迁移中完成所有操作.注意:这适用于您尚未在数据库中拥有记录的情况.
class AddDivisionIdToProfile < ActiveRecord::Migration def self.up add_column :profiles, :division_id, :integer change_column :profiles, :division_id, :integer, :null => false end def self.down remove_column :profiles, :division_id end end
我们在没有NOT NULL约束的情况下添加列,然后立即更改列以添加约束.我们可以这样做,因为虽然SQLite在列添加期间显然非常关注,但是对于列更改它并不那么挑剔.这是我书中清晰的设计气味.
它绝对是一个黑客,但它比多次迁移更短,它仍然适用于生产环境中更强大的SQL数据库.
您已经在表中有行,并且您正在添加新列division_id
.它需要每个现有行中的新列中的某些内容.
SQLite通常会选择NULL,但是你已经指定它不能为NULL,那么应该是什么呢?它无从知晓.
看到:
在Rails迁移中添加没有默认值的非空列(2009年,不再可用,因此这是archive.org上的快照)
将NOT NULL列添加到现有表(2014)
该博客的建议是添加没有非null约束的列,并且每行都会添加NULL.然后您可以在中填入值division_id
,然后使用change_column
添加非空约束.
请参阅我链接到的博客,以获取执行此三个步骤的迁移脚本的说明.
如果您有一个包含现有行的表,则需要在添加null
约束之前更新现有行.在上迁移指南推荐使用局部模型,如下所示:
Rails 4及以上:
class AddDivisionIdToProfile < ActiveRecord::Migration class Profile < ActiveRecord::Base end def change add_column :profiles, :division_id, :integer Profile.reset_column_information reversible do |dir| dir.up { Profile.update_all division_id: Division.first.id } end change_column :profiles, :division_id, :integer, :null => false end end
Rails 3
class AddDivisionIdToProfile < ActiveRecord::Migration class Profile < ActiveRecord::Base end def change add_column :profiles, :division_id, :integer Profile.reset_column_information Profile.all.each do |profile| profile.update_attributes!(:division_id => Division.first.id) end change_column :profiles, :division_id, :integer, :null => false end end