MySQL: 损坏的外键实现?
MySQL: Broken foreign key implementaion?
在 MySQL 中定义外键的内联语法似乎没有做任何事情,而较长的 CONSTRAINT 语法似乎按预期工作。我很好奇为什么会这样。
我最近发现,我认为可以正常工作的用于定义外键的内联语法不会对参照完整性进行任何实际检查。 ON DELETE/UPDATE 子句中定义的内容无关紧要。
我当然用 InnoDB 尝试过这个,因为我知道 MyISAM 不支持外键检查。
请看一下 examples/fiddles 你就会明白我的意思了。
内联语法
这是我多年来使用的文档定义外键的正确方法。
-- Create a basic foreign key relationship.
CREATE TABLE `parent` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`a` VARCHAR(255) NOT NULL
)ENGINE=InnoDB; -- Just to be sure.
CREATE TABLE `child` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
-- Short legitimate syntax, frequently used.
`parent_id` INTEGER UNSIGNED NOT NULL REFERENCES `parent` (`id`)
ON DELETE RESTRICT
)ENGINE=InnoDB; -- Just to be sure.
现在,如果我从 parent table 中删除一行,它在 child table 中被引用],就像我从来没有定义过外键一样,它被删除了。
亲自尝试一下:Fiddle
CONSTRAINT 语法
使用 CONSTRAINT 关键字的较长语法编写起来更麻烦,但与内联定义相比似乎可以按预期工作。
-- `parent` table has been omitted, since it is the same as above.
CREATE TABLE `child` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`parent_id` INTEGER UNSIGNED NOT NULL,
-- Longer, more cumbersome syntax.
CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
ON DELETE RESTRICT
)ENGINE=InnoDB; -- Just to be sure.
如果我尝试从 parent 中删除引用的行,它会按预期失败。
自己尝试一下:Fiddle(您必须取消注释左侧的最后一条语句才能看到它失败)
Conclusion/Actual 问题
正如您在小提琴中亲眼看到的那样,用于创建外键的内联语法似乎没有任何作用,而较长的 CONSTRAINT 语法可以正常工作。
有谁知道为什么会这样吗?有什么原因吗?或者这只是我们必须解决的另一个 MySQL 怪癖?
请分享你的知识,我很好奇。
Update/SHOW 创建 TABLE 验证
这是@MikePurcell 指出的 SHOW CREATE TABLE
的输出。
"Short" 语法
CREATE TABLE `child`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CONSTRAINT 语法
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_child_parent` (`parent_id`),
CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
现在很明显,解析器使用 "short" 语法简单地忽略了 table 定义中的 REFERENCES
子句。至少文档更新会有所帮助。感谢大家的帮助。
仅供参考: 这已在 2004 年 (Bug #4919) 中作为错误归档,并且似乎已知时间更长。我真的希望他们至少会更新有关此的文档,因为我认为这不会很快得到解决。
我以前唯一知道的 "short syntax" 是:
create_definition:
col_name column_definition
...
| [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (index_col_name,...) reference_definition
reference_definition:
REFERENCES tbl_name (index_col_name,...)
[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
[ON DELETE reference_option]
[ON UPDATE reference_option]
注意强制性的 FOREIGN KEY
条款。
但是,解析器确实接受您使用的“更短 语法”,in accordance with the manual:
column_definition:
data_type [NOT NULL | NULL] [DEFAULT default_value]
...
[reference_definition]
注意缺少 FOREIGN KEY
子句。
事实上,这种奇怪的行为记录在同一页中:
MySQL parses but ignores “inline REFERENCES
specifications” (as
defined in the SQL standard) where the references are defined as part
of the column specification. MySQL accepts REFERENCES
clauses only
when specified as part of a separate FOREIGN KEY
specification.
也对限制进行了更广泛的讨论 in the tutorial。
这是错误还是缺失的功能 has been long debated,看来是这样。
在 MySQL 中定义外键的内联语法似乎没有做任何事情,而较长的 CONSTRAINT 语法似乎按预期工作。我很好奇为什么会这样。
我最近发现,我认为可以正常工作的用于定义外键的内联语法不会对参照完整性进行任何实际检查。 ON DELETE/UPDATE 子句中定义的内容无关紧要。
我当然用 InnoDB 尝试过这个,因为我知道 MyISAM 不支持外键检查。
请看一下 examples/fiddles 你就会明白我的意思了。
内联语法
这是我多年来使用的文档定义外键的正确方法。
-- Create a basic foreign key relationship.
CREATE TABLE `parent` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`a` VARCHAR(255) NOT NULL
)ENGINE=InnoDB; -- Just to be sure.
CREATE TABLE `child` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
-- Short legitimate syntax, frequently used.
`parent_id` INTEGER UNSIGNED NOT NULL REFERENCES `parent` (`id`)
ON DELETE RESTRICT
)ENGINE=InnoDB; -- Just to be sure.
现在,如果我从 parent table 中删除一行,它在 child table 中被引用],就像我从来没有定义过外键一样,它被删除了。 亲自尝试一下:Fiddle
CONSTRAINT 语法
使用 CONSTRAINT 关键字的较长语法编写起来更麻烦,但与内联定义相比似乎可以按预期工作。
-- `parent` table has been omitted, since it is the same as above.
CREATE TABLE `child` (
`id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`parent_id` INTEGER UNSIGNED NOT NULL,
-- Longer, more cumbersome syntax.
CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
ON DELETE RESTRICT
)ENGINE=InnoDB; -- Just to be sure.
如果我尝试从 parent 中删除引用的行,它会按预期失败。 自己尝试一下:Fiddle(您必须取消注释左侧的最后一条语句才能看到它失败)
Conclusion/Actual 问题
正如您在小提琴中亲眼看到的那样,用于创建外键的内联语法似乎没有任何作用,而较长的 CONSTRAINT 语法可以正常工作。
有谁知道为什么会这样吗?有什么原因吗?或者这只是我们必须解决的另一个 MySQL 怪癖?
请分享你的知识,我很好奇。
Update/SHOW 创建 TABLE 验证
这是@MikePurcell 指出的 SHOW CREATE TABLE
的输出。
"Short" 语法
CREATE TABLE `child`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CONSTRAINT 语法
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_child_parent` (`parent_id`),
CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
现在很明显,解析器使用 "short" 语法简单地忽略了 table 定义中的 REFERENCES
子句。至少文档更新会有所帮助。感谢大家的帮助。
仅供参考: 这已在 2004 年 (Bug #4919) 中作为错误归档,并且似乎已知时间更长。我真的希望他们至少会更新有关此的文档,因为我认为这不会很快得到解决。
我以前唯一知道的 "short syntax" 是:
create_definition: col_name column_definition ... | [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) reference_definition reference_definition: REFERENCES tbl_name (index_col_name,...) [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] [ON DELETE reference_option] [ON UPDATE reference_option]
注意强制性的 FOREIGN KEY
条款。
但是,解析器确实接受您使用的“更短 语法”,in accordance with the manual:
column_definition: data_type [NOT NULL | NULL] [DEFAULT default_value] ... [reference_definition]
注意缺少 FOREIGN KEY
子句。
事实上,这种奇怪的行为记录在同一页中:
MySQL parses but ignores “inline
REFERENCES
specifications” (as defined in the SQL standard) where the references are defined as part of the column specification. MySQL acceptsREFERENCES
clauses only when specified as part of a separateFOREIGN KEY
specification.
也对限制进行了更广泛的讨论 in the tutorial。
这是错误还是缺失的功能 has been long debated,看来是这样。