无法在 Sqlite3 中添加默认值为 NULL 的 NOT NULL 列
尝试将 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”整数 NOT NULL
I am getting the following error while trying to add a NOT NULL column to an existing table. Why is it happening ?. I tried rake db:reset thinking that the existing records are the problem, but even after resetting the DB, the problem persists. Can you please help me figure this out.
Migration File
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
Error Message
SQLite3::SQLException: Cannot add a NOT NULL column with default value NULL: ALTER TABLE "profiles" ADD "division_id" integer NOT NULL
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是(我认为的)SQLite 的一个小故障。无论表中是否有记录,都会发生此错误。
从头开始添加表时,您可以指定 NOT NULL,这就是您使用“:null => false”符号所做的事情。但是,添加列时不能执行此操作。 SQLite 的规范规定您必须为此设置一个默认值,这是一个糟糕的选择。添加默认值不是一个选项,因为它违背了使用 NOT NULL 外键的目的 - 即数据完整性。
这是解决此问题的一种方法,您可以在同一次迁移中完成这一切。注意:这是针对数据库中还没有记录的情况。
我们添加不带 NOT NULL 约束的列,然后立即更改该列以添加约束。我们可以这样做,因为虽然 SQLite 在列添加过程中显然非常关心,但它对列更改并不那么挑剔。这是我书中明显的设计味道。
这绝对是一种 hack,但它比多次迁移要短,并且仍然可以在生产环境中与更强大的 SQL 数据库配合使用。
This is (what I would consider) a glitch with SQLite. This error occurs whether there are any records in the table or not.
When adding a table from scratch, you can specify NOT NULL, which is what you're doing with the ":null => false" notation. However, you can't do this when adding a column. SQLite's specification says you have to have a default for this, which is a poor choice. Adding a default value is not an option because it defeats the purpose of having a NOT NULL foreign key - namely, data integrity.
Here's a way to get around this glitch, and you can do it all in the same migration. NOTE: this is for the case where you don't already have records in the database.
We're adding the column without the NOT NULL constraint, then immediately altering the column to add the constraint. We can do this because while SQLite is apparently very concerned during a column add, it's not so picky with column changes. This is a clear design smell in my book.
It's definitely a hack, but it's shorter than multiple migrations and it will still work with more robust SQL databases in your production environment.
表中已有行,并且要添加新列
division_id
。它需要在每个现有行的新列中添加一些内容。SQLite 通常会选择 NULL,但您已指定它不能为 NULL,那么它应该是什么?它没有办法知道。
请参阅:
该博客的建议是添加不带非空约束的列,并且将在每一行中添加 NULL。然后,您可以在
division_id
中填写值,然后使用change_column
添加非空约束。请参阅我链接到的博客,了解执行此三步过程的迁移脚本的说明。
You already have rows in the table, and you're adding a new column
division_id
. It needs something in that new column in each of the existing rows.SQLite would typically choose NULL, but you've specified it can't be NULL, so what should it be? It has no way of knowing.
See:
That blog's recommendation is to add the column without the not null constraint, and it'll be added with NULL in every row. Then you can fill in values in the
division_id
and then usechange_column
to add the not null constraint.See the blogs I linked to for an description of a migration script that does this three-step process.
您可以添加具有默认值的列:
You can add a column with a default value:
如果您的表包含现有行,那么您需要在添加
null
约束之前更新现有行。 迁移指南建议使用本地模型,如下所示:Rails 4 及更高版本:
轨道 3
If you have a table with existing rows then you will need to update the existing rows before adding your
null
constraint. The Guide on migrations recommends using a local model, like so:Rails 4 and up:
Rails 3
以下迁移在 Rails 6 中对我有用:
注意第一行中的
:division
和第二行中的:division_id
change_column_null
的 API 文档The following migration worked for me in Rails 6:
Note
:division
in the first line and:division_id
in the secondAPI Doc for
change_column_null
不要忘记,要求使用 ALTER TABLE ADD COLUMN NOT NULL 的默认值也有一些积极的作用,至少在将列添加到包含现有数据的表中时是这样。如 https://www.sqlite.org/lang_altertable.html#altertabaddcol 中所述:
文件格式本身支持此 https://www.sqlite.org/fileformat.html
使用此技巧,可以通过仅更新架构来添加新列,该操作在包含 670 万行的测试表中花费了 387 毫秒。数据区中的现有记录根本不被触及,节省的时间是巨大的。添加列的缺失值来自架构,如果没有另外说明,默认值为 NULL。如果新列不为 NULL,则必须将默认值设置为其他值。
我不知道为什么当表为空时没有 ALTER TABLE ADD COLUMN NOT NULL 的特殊路径。一个好的解决方法也许是从一开始就创建表。
Not to forget that there is also something positive in requiring the default value with ALTER TABLE ADD COLUMN NOT NULL, at least when adding a column into a table with existing data. As documented in https://www.sqlite.org/lang_altertable.html#altertabaddcol:
The file format itself has support for this https://www.sqlite.org/fileformat.html
With this trick it is possible to add a new column by updating just the schema, operation that took 387 milliseconds with a test table having 6.7 million rows. The existing records in the data area are not touched at all and the time saving is huge. The missing values for the added column come on-the-fly from the schema and the default value is NULL if not otherwise stated. If the new column is NOT NULL then the default value must be set to something else.
I do not know why there is not a special path for ALTER TABLE ADD COLUMN NOT NULL when the table is empty. A good workaround is perhaps to create the table right from the beginning.