当前位置:  开发笔记 > 数据库 > 正文

如何在SQLite3中解决"无法在默认值NULL中添加NOT NULL列"?

如何解决《如何在SQLite3中解决"无法在默认值NULL中添加NOTNULL列"?》经验,为你挑选了3个好方法。

尝试将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数据库.



1> Jaime Bellmy..:

这是(我会考虑的)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中没有ALTER COLUMN.
@BrianOrtiz,使用SQLite进行ALTER COLUMN的技术令人费解.您需要将表复制到临时表,删除现有表,使用修改后的列重新创建表,将临时表数据复制到新创建的表,最后删除临时表.这很可能是ROR所做的.而且,SQLite没有这种内置功能也是愚蠢的.

2> Bill Karwin..:

您已经在表中有行,并且您正在添加新列division_id.它需要每个现有行中的新列中的某些内容.

SQLite通常会选择NULL,但是你已经指定它不能为NULL,那么应该是什么呢?它无从知晓.

看到:

在Rails迁移中添加没有默认值的非空列(2009年,不再可用,因此这是archive.org上的快照)

将NOT NULL列添加到现有表(2014)

该博客的建议是添加没有非null约束的列,并且每行都会添加NULL.然后您可以在中填入值division_id,然后使用change_column添加非空约束.

请参阅我链接到的博客,以获取执行此三个步骤的迁移脚本的说明.


你对表格中已有行的假设听起来是正确的,而且几乎任何其他rdbms都是如此.但是,我在回答中指出SQLite是一个例外.即使表是空的,也会出现此错误,因此我发布了一个较短的解决方案.

3> JosephL..:

如果您有一个包含现有行的表,则需要在添加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

推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有