Mysql和FK的问题

时间:2011-09-09 21:12:59

标签: php mysql foreign-keys constraints

我在这里找不到一些表格。

我有这张桌子:

CREATE TABLE `smenuitem` (
    `nome` VARCHAR(150) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `url` VARCHAR(150) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `tipo` CHAR(4) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `ordemmenu` INT(10) NULL DEFAULT NULL,
    `codparent` INT(10) UNSIGNED NOT NULL,
    `codmenuitem` INT(10) UNSIGNED NOT NULL,
    `codmodulo` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (`codmodulo`, `codmenuitem`, `codmenuitem2`),
    CONSTRAINT `FK_smenuitem_smodulos` FOREIGN KEY (`codmodulo`) REFERENCES `smodulos` (`codmodulo`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
ROW_FORMAT=DEFAULT

还有第二个:

CREATE TABLE `smenuitememp` (
    `codempresa` INT(10) UNSIGNED NOT NULL,
    `codmodulo` INT(10) UNSIGNED NOT NULL,
    `codmenuitem` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (`codmenuitem`, `codempresa`, `codmodulo`)
)
COLLATE='utf8_unicode_ci'

我的问题是我需要在codmenuitem之间制作一个FK 我有这个sql命令导致错误:

ALTER TABLE `smenuitememp`  ADD CONSTRAINT `FK_smenuitememp_smenuitem` FOREIGN KEY (`codmenuitem`) REFERENCES `smenuitem` (`codmenuitem`);

当我尝试执行它时会返回此错误:

enter image description here

有人有想法吗?


更新......我正试图解决问题,并得到了一个新问题...... T_T

CREATE TABLE `smenuitem` (
    `nome` VARCHAR(150) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `url` VARCHAR(150) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `tipo` CHAR(4) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `ordemmenu` INT(10) NULL DEFAULT NULL,
    `codparent` INT(10) UNSIGNED NOT NULL,
    `codmenuitem` INT(10) UNSIGNED NOT NULL,
    `codmodulo` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (`codmodulo`, `codmenuitem`),
    INDEX `codmenuitem` (`codmenuitem`),
    CONSTRAINT `FK_smenuitem_smodulos` FOREIGN KEY (`codmodulo`) REFERENCES `smodulos` (`codmodulo`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
ROW_FORMAT=DEFAULT

我解决了在主表创建索引的问题。但我不知道为什么没有这个索引我遇到了麻烦。如果有人能问我,我会赞成!

1 个答案:

答案 0 :(得分:2)

外键列必须引用包含主键最左前缀或父表中唯一键的列。

换句话说,以下示例适用于InnoDB:

CREATE TABLE Foo ( a INT, b INT, c INT, PRIMARY KEY (a,b,c) );
CREATE TABLE Bar ( x INT, y INT );

ALTER TABLE Bar ADD FOREIGN KEY (x,y) REFERENCES Foo(b,c); -- WRONG

ALTER TABLE Bar ADD FOREIGN KEY (x,y) REFERENCES Foo(a,c); -- WRONG

ALTER TABLE Bar ADD FOREIGN KEY (x,y) REFERENCES Foo(a,b); -- RIGHT

ALTER TABLE Bar ADD FOREIGN KEY (x) REFERENCES Foo(b); -- WRONG

ALTER TABLE Bar ADD FOREIGN KEY (x) REFERENCES Foo(a); -- RIGHT

你得到一个错误,因为你试图做相当于(x)引用的Foo(b) 您的列codmenuitem是父级主键中三列中的第二列。

如果smenuitememp.codemenuitem引用smenuitem.codmodulo,它将起作用,因为该列是父表主键中最左边的列。


重新提出你的后续问题:

请记住外键的工作方式。每次在子表中插入或更新行时,都需要在父表中查找一行以验证该值是否存在于引用的列中。如果列没有编入索引,则必须进行表扫描以实现此查找,并且假设父表增长,这将非常昂贵。

如果您尝试根据多列索引的中间列查找行,则索引对您没有帮助。通过类比,就像在电话簿中搜索具有某个中间名的所有人一样。

标准ANSI SQL要求引用的列是PRIMARY KEY或UNIQUE KEY的一部分,并且它要求外键列匹配 all 父级中的主要或唯一约束的列。

但InnoDB更宽容。它仍然需要对父表中引用的列进行索引,以便查找可以高效,引用的列是索引中最左边的列。但是一个非独特的指数是可以的;它允许外键引用它。

这可能导致奇怪的情况,如子行,引用父对象中的多行,但预计您将处理此类异常。


我觉得有必要强调最后一点。如果您为父级中的非唯一索引列定义外键,则 获取异常数据。这可能会导致您的查询在您进行连接时多次报告行。你不应该使用InnoDB的这种行为;您应该只将外键定义为唯一的父列。