如何将外键添加到现有的SQLite表中?


128

我有下表:

CREATE TABLE child( 
  id INTEGER PRIMARY KEY, 
  parent_id INTEGER, 
  description TEXT);

如何在上添加外键约束parent_id?假设启用了外键。

大多数示例都假设您正在创建表-我想将约束添加到现有表中。


SQLite ALTER命令仅支持“重命名表”和“添加列”。但是,我们可以使用简单的操作序列对表的格式进行其他任意更改。检查我的答案
situee

Answers:


198

你不能

尽管向表中添加外键的SQL-92语法如下:

ALTER TABLE child ADD CONSTRAINT fk_child_parent
                  FOREIGN KEY (parent_id) 
                  REFERENCES parent(id);

SQLite不支持该命令的ADD CONSTRAINT变体ALTER TABLEsqlite.org:SQLite未实现的SQL功能)。

因此,在sqlite 3.6.1中添加外键的唯一方法CREATE TABLE如下:

CREATE TABLE child ( 
    id           INTEGER PRIMARY KEY, 
    parent_id    INTEGER, 
    description  TEXT,
    FOREIGN KEY (parent_id) REFERENCES parent(id)
);

不幸的是,您将必须将现有数据保存到临时表中,删除旧表,使用FK约束创建新表,然后将数据从临时表中复制回去。(sqlite.org-FAQ:Q11


28
我认为重命名旧表,创建新表并将数据复制回更容易。然后可以删除旧表。
tuinstoel

是的,那很容易。我只是引用了sqlite常见问题解答:sqlite.org/faq.html#q11。事实上,RENAME TO是为数不多ALTER TABLE当前在SQLite的3支持变种
丹尼尔·瓦萨洛

3
不应该这样:FOREIGN KEY(parent_id)参考parent(id)是的,乔纳森(Jonathan)没有提供“父表”的名称。实际上,该表应命名为person,但...
igorludi 2012年

3
对我来说,这似乎是一个大问题。通常,在转储数据库时,首先要导出CREATE TABLE命令。然后是INSERT INTO命令,最后是ADD CONSTRAINT命令。如果数据中存在循环(外键值)依赖性,则在强制使用外键时无法插入数据。但是,如果以后无法添加外键约束,那么您将陷入困境。当然存在延迟的约束,但这非常笨拙。
nagylzs 2014年

9
如果其他表引用了该表,则不要按照第一条评论中的说明重命名该表!在这种情况下,您也将不得不重新创建所有这些表。
rocknow

57

如果您更改表并添加使用约束的列,则可以添加约束。

首先,创建不带parent_id的表:

CREATE TABLE child( 
  id INTEGER PRIMARY KEY,  
  description TEXT);

然后,修改表:

ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);

2
习惯于此序列很不错,但这并不能回答实际的问题:我想将约束添加到现有约束中。

9

请检查https://www.sqlite.org/lang_altertable.html#otheralter

SQLite直接支持的唯一模式更改命令是上面显示的“重命名表”和“添加列”命令。但是,应用程序可以使用简单的操作序列对表的格式进行其他任意更改。对某些表X的架构设计进行任意更改的步骤如下:

  1. 如果启用了外键约束,请使用PRAGMA foreign_keys = OFF禁用它们。
  2. 开始交易。
  3. 记住与表X关联的所有索引和触发器的格式。在下面的步骤8中将需要此信息。一种方法是运行如下查询:SELECT type,sql FROM sqlite_master WHERE tbl_name ='X'。
  4. 使用CREATE TABLE构造一个新的表“ new_X”,该表具有表X的所需修订格式。当然,请确保名称“ new_X”不与任何现有表名冲突。
  5. 使用以下语句将内容从X传输到new_X中:INSERT INTO new_X SELECT ... FROMX。
  6. 删除旧表X:DROP TABLEX。
  7. 使用以下命令将new_X的名称更改为X:ALTER TABLE new_X RENAME TOX。
  8. 使用CREATE INDEX和CREATE TRIGGER来重建与表X关联的索引和触发器。也许可以使用上面第3步中保存的触发器和索引的旧格式作为指南,并根据更改进行适当的更改。
  9. 如果有任何视图以受架构更改影响的方式引用表X,请使用DROP VIEW删除这些视图,并使用CREATE VIEW进行必要的任何更改以重新创建它们以适应架构更改。
  10. 如果最初启用了外键约束,则运行PRAGMA foreign_key_check以验证架构更改没有破坏任何外键约束。
  11. 提交在步骤2中开始的事务。
  12. 如果最初启用了外键约束,请立即重新启用它们。

上面的过程是完全通用的,即使架构更改导致表中存储的信息发生更改,该过程也将起作用。因此,例如,上面的完整过程适用于删除列,更改列的顺序,添加或删除UNIQUE约束或PRIMARY KEY,添加CHECK或FOREIGN KEY或NOT NULL约束或更改列的数据类型。


4

是的,您可以,而无需添加新列。您必须小心正确地执行此操作,以避免损坏数据库,因此在尝试执行此操作之前,应完全备份数据库。

对于您的特定示例:

CREATE TABLE child(
  id INTEGER PRIMARY KEY,
  parent_id INTEGER,
  description TEXT
);

--- create the table we want to reference
create table parent(id integer not null primary key);

--- now we add the foreign key
pragma writable_schema=1;
update SQLITE_MASTER set sql = replace(sql, 'description TEXT)',
    'description TEXT, foreign key (parent_id) references parent(id))'
) where name = 'child' and type = 'table';

--- test the foreign key
pragma foreign_keys=on;
insert into parent values(1);
insert into child values(1, 1, 'hi'); --- works
insert into child values(2, 2, 'bye'); --- fails, foreign key violation

或更一般而言:

pragma writable_schema=1;

// replace the entire table's SQL definition, where new_sql_definition contains the foreign key clause you want to add
UPDATE SQLITE_MASTER SET SQL = new_sql_definition where name = 'child' and type = 'table';

// alternatively, you might find it easier to use replace, if you can match the exact end of the sql definition
// for example, if the last column was my_last_column integer not null:
UPDATE SQLITE_MASTER SET SQL = replace(sql, 'my_last_column integer not null', 'my_last_column integer not null, foreign key (col1, col2) references other_table(col1, col2)') where name = 'child' and type = 'table';

pragma writable_schema=0;

无论哪种方式,在进行任何更改之前,您可能都希望首先了解SQL定义是什么:

select sql from SQLITE_MASTER where name = 'child' and type = 'table';

如果使用replace()方法,可能会发现在执行之前先运行以下命令来测试replace()命令会很有帮助:

select replace(sql, ...) from SQLITE_MASTER where name = 'child' and type = 'table';

3

如果您使用的是Firefox附加sqlite-manager,则可以执行以下操作:

无需删除并再次创建表,您可以像这样修改它。

在“列”文本框中,右键单击列出的最后一个列名称以显示上下文菜单,然后选择“编辑列”。请注意,如果TABLE定义中的最后一列是PRIMARY KEY,则必须首先添加一个新列,然后编辑该新列的列类型,以便添加FOREIGN KEY定义。在“列类型”框中,添加逗号和

FOREIGN KEY (parent_id) REFERENCES parent(id)

数据类型后的定义。单击“更改”按钮,然后在“危险操作”对话框上单击“是”按钮。

参考: Sqlite管理器


2

您可以尝试以下方法:

ALTER TABLE [Child] ADD COLUMN column_name INTEGER REFERENCES parent_table_name(column_id);

-1

基本上,您不能但可以绕过这种情况。

将外键约束添加到现有表的正确方法是以下命令。

db.execSQL("alter table child add column newCol integer REFERENCES parent(parent_Id)");

然后将parent_Id数据复制到newCol,然后删除Parent_Id列。因此,不需要临时表。


看来您没有仔细阅读问题。问题是仅添加外部约束,而不是添加具有约束的列。

不。它不能回答所问的问题。
MK

-4

首先在添加子表列Cidint然后alter table用下面的代码。这样,您可以将外键添加Cid为父表的主键,并将其用作子表中的外键...希望对您有帮助,因为它对我有好处:

ALTER TABLE [child] 
  ADD CONSTRAINT [CId] 
  FOREIGN KEY ([CId]) 
  REFERENCES [Parent]([CId]) 
  ON DELETE CASCADE ON UPDATE NO ACTION;
GO

1
这在SQLite中无效。这也是MS SQL语法。
StilesCrisis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.