外键可以引用非唯一索引吗?

时间:2009-02-26 01:29:54

标签: mysql sql foreign-keys

我认为外键意味着单行必须引用单行,但我正在查看一些表肯定不是这样的表。 Table1的column1在table2中的column2上有一个外键约束,但是table2中有许多记录在column2中有相同的值。 column2上还有非唯一索引。这是什么意思?外键约束是否只是意味着在右列中必须存在至少一个具有正确值的记录?我认为这意味着必须有一个这样的记录(不确定空白是如何适应图片的,但我现在不太关心)。

更新:显然,这种行为是特定于MySQL的,这是我正在使用的,但我没有在我原来的问题中提到它。

7 个答案:

答案 0 :(得分:44)

来自MySQL documentation

  

InnoDB允许外键约束引用非唯一键。这是标准SQL的InnoDB扩展。

但是,有一个实际的理由要避免在引用表的非唯一列上使用外键。也就是说,在这种情况下,“ON DELETE CASCADE”的语义应该是什么?

The documentation further advises

  

对非唯一键或包含NULL值的键的外键引用的处理没有很好地定义(...)建议您使用仅引用UNIQUE(包括PRIMARY)和NOT NULL键的外键。

答案 1 :(得分:7)

您的分析是正确的;键不必是唯一的,约束将作用于匹配的行集。通常不是一种有用的行为,但情况可能出现在你想要的地方。

答案 2 :(得分:3)

是的,您可以为任何表中的任何列创建外键。但是,大多数时候你会将它们创建为主键。

如果确实使用了未指向主键的外键,那么为了提高性能,您可能还需要为正在引用的列创建一个(非唯一)索引。

取决于您正在使用的RDBMS。我认为有些人暗中这样做,或者使用其他一些技巧。 RTM。

答案 3 :(得分:3)

发生这种情况时,通常意味着两个外键相互链接。 通常,包含密钥作为主键的表甚至不在模式中。

示例:两个表(COLLEGES和STUDENTS)都包含一个名为ZIPCODE的列。

如果我们快速检查

SELECT * FROM COLLEGES JOIN STUDENTS ON COLLEGES.ZIPCODE = STUDENTS.ZIPCODE

我们可能会发现这种关系很多很多。如果我们的模式有一个名为ZIPCODES的表,主键为ZIPCODE,那么显而易见的是什么。

但我们的架构没有这样的表格。仅仅因为我们的模式没有这样的表并不意味着这样的数据不存在。某个地方,在USPO土地上,只有这样一张桌子。 COLLEGES.ZIPCODE和STUDENTS.ZIPCODE都是对该表的引用,即使我们不承认它。

这更多地与数据哲学相关,而不是建立数据库的实践,但它巧妙地说明了一些基本的东西:数据具有我们发现的特征,而不仅仅是我们发明的特征。当然,我们发现的可能是别人发明的东西。 ZIPCODE就是这种情况。

答案 4 :(得分:1)

PostgreSQL也拒绝这一点(无论如何,即使它可能,但这并不意味着它是个好主意):

essais=> CREATE TABLE Cities (name TEXT, country TEXT);
CREATE TABLE
essais=> INSERT INTO Cities VALUES ('Syracuse', 'USA');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Syracuse', 'Greece');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Aramits', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'USA');
INSERT 0 1

essais=> CREATE TABLE People (name TEXT, city TEXT REFERENCES Cities(name));
ERROR:  there is no unique constraint matching given keys for referenced table "cities"

答案 5 :(得分:0)

我们在谈论什么数据库?在SQL 2005中,我无法创建引用没有唯一约束(主键或其他)的列的外键约束。

create table t1
(
  id int identity,
  fk int
);

create table t2
(
  id int identity,
);

CREATE NONCLUSTERED INDEX [IX_t2] ON [t2] 
(
    [id] ASC
);
ALTER TABLE t1 with NOCHECK
ADD CONSTRAINT FK_t2 FOREIGN KEY (fk)
    REFERENCES t2 (id) ;


Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 't2' 
that match the referencing column list in the foreign key 'FK_t2'.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.

如果你真的可以这样做,你就会有效地建立多对多关系,如果没有中间表,这是不可能的。我真的很想听到更多关于这个......

请参阅this related question和答案。

答案 6 :(得分:0)

Necromancing。
正如其他人已经说过的那样,您不应该将非唯一键引用为外键。
但你可以做的是(没有删除级联危险)是添加一个检查约束(至少在MS-SQL中)。
这与外键不完全相同,但至少它会阻止插入无效/孤立/死数据。

请参阅此处以供参考(您必须将MS-SQL代码移植到MySQL语法):
Foreign Key to non-primary key

修改
根据{{​​3}}搜索downvote的原因,MySQL并不真正支持CHECK约束。
您可以出于兼容性原因在DDL查询中定义它们,但它们只是被忽略......

但正如那里所提到的,你可以创建一个BEFORE INSERTBEFORE UPDATE触发器,当不满足数据的要求时会抛出错误,这基本上是相同的,除了它是更糟糕的是。

至于问题:

  

我认为外键意味着一行必须引用一个   单排,但我正在看一些肯定是这样的表   不是这样的。

在任何理智的RDBMS中,这都是事实。
在MySQL中这是可能的事实只是为什么
的另一个原因 MySQL是一种理解的RDBMS 它可能很快,但在速度祭坛上牺牲参照完整性和数据质量并不是我对质量rdbms的看法。
事实上,如果它不符合ACID标准,它根本不是(正确运行的)RDBMS。