如何解决SQLite3中不能添加一个默认值NULL的NOT NULL列?
在尝试将NOT NULL列添加到现有表时遇到以下错误。 为什么发生? 我试图耙db:重置认为现有的logging是问题,但即使重置数据库后,问题仍然存在。 你能帮我解决这个问题吗?
迁移文件
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:无法添加具有默认值的NOT NULL列NULL:ALTER TABLE“profiles”ADD“division_id”integer NOT NULL
表格中已经有了行,并且正在添加一个新的列division_id
。 它需要每个现有行中的新列中的某些内容。
SQLite通常会selectNULL,但你已经指定它不能为NULL,那么它应该是什么? 它无法知道。
请参阅在Rails迁移中添加没有默认值的非空列
该博客的build议是添加没有非空约束的列,并在每行中添加NULL。 然后你可以填写division_id
中的值,然后使用change_column
添加非空约束。
请参阅我链接到的博客,以获取执行此三步过程的迁移脚本示例。
这是(我会考虑)与SQLite的一个小故障。 无论表中是否有任何logging,都会发生此错误。
当从头开始添加一个表时,你可以指定NOT NULL,这就是你用“:null => false”表示法所做的事情。 但是,添加列时不能这样做。 SQLite的规范说,你必须有一个默认的,这是一个糟糕的select。 添加默认值不是一个选项,因为它违背了具有NOT NULL外键的目的 – 即数据完整性。
这是解决这个问题的一种方法,你可以在同一个迁移中完成这一切。 注意:这是在数据库中没有logging的情况下。
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在添加列时显然非常担心,但对列更改并不那么挑剔。 这是我书中清晰的devise气味。
这绝对是一个黑客,但它比多个迁移短,它仍然可以在您的生产环境中与更健壮的SQL数据库一起工作。
如果你有一个包含现有行的表,那么在添加null
约束之前,你需要更新现有的行。 迁移指南build议使用本地模型,如下所示:
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