是否可以在数据库中为外键创建唯一的字段?

Posted

技术标签:

【中文标题】是否可以在数据库中为外键创建唯一的字段?【英文标题】:Is possible to make unique a field in the database for a foreign key? 【发布时间】:2021-06-28 22:48:29 【问题描述】:

假设我有两个模型,分别命名为 Company 和 Airplane:

class Company(models.Model):
    name= models.CharField(max_length=250)
    location=models.Charfield(max_length=250)

class Airplane(models.Model):  
    company = models.ForeignKey(Company, on_delete=models.RESTRICT)
    number = models.IntegerField(unique=True)

我希望飞机型号中的编号是唯一的,但只是在同一家公司,另一家公司的飞机可以有相同的编号。这可能吗?

【问题讨论】:

【参考方案1】:

您可以强制执行多列唯一性约束,而不是要求飞机编号字段是唯一的。换句话说,(company, number) 对应该是唯一的,并且另一个默认的自动递增列可以用作 Airplane 表的主键。

在此处查看答案: How to define two fields "unique" as couple

【讨论】:

【参考方案2】:

您可以为由 company 和 number 列组成的飞机表创建一个 UNIQUE Index(至少在本机 SQLite 中)。

我没有使用过 Django,所以我不确定是否可以使用多列索引。

看来您可以按照Django composite unique on multiple model fields 在DJango 中使用unique_togetherUniqueConstrainthttps://docs.djangoproject.com/en/3.1/ref/models/constraints/#django.db.models.UniqueConstraint

但是,对于本机 SQLite 代码,这里有一些演示基本原理/操作的代码:-

DROP INDEX IF EXISTS airplane_number_company;
DROP TABLE IF EXISTS airplane;
DROP TABLE IF EXISTS company;
CREATE TABLE IF NOT EXISTS company (name TEXT UNIQUE, location TEXT);
CREATE TABLE IF NOT EXISTS airplane (company REFERENCES company(name), number INTEGER);
CREATE UNIQUE INDEX IF NOT EXISTS airplane_number_company ON airplane(company,number);

INSERT INTO company VALUES('company1','England'),('company2','America');
INSERT INTO airplane VALUES('company1',100),('company1',200),('company2',100),('company2',200);
SELECT * FROM airplane JOIN company ON airplane.company = company.name;
INSERT OR IGNORE INTO airplane VALUES('company1',100),('company1',500);
SELECT * FROM airplane JOIN company ON airplane.company = company.name;

第一个 SELECT 将显示已添加的 4 行,表明可以添加具有相同编号但不同公司的飞机。

在选择之后尝试插入两个新的飞机行。一行使用与现有飞机相同的公司和编号,另一行使用未使用的组合。

然后第二个 SELECT 显示结果,表明仅添加了两行中的 1 行。由于 airplane_number_company 索引 OR IGNORE 允许忽略失败的尝试,因此无法添加第一行。

上面运行的日志是:-

DROP INDEX IF EXISTS airplane_number_company
> OK
> Time: 0s


DROP TABLE IF EXISTS airplane
> OK
> Time: 0s

DROP TABLE IF EXISTS company
> OK
> Time: 0s

CREATE TABLE IF NOT EXISTS company (name TEXT UNIQUE, location TEXT)
> OK
> Time: 0.11s
   
CREATE TABLE IF NOT EXISTS airplane (company REFERENCES company(name), number INTEGER)
> OK
> Time: 0.096s
    
CREATE UNIQUE INDEX IF NOT EXISTS airplane_number_company ON airplane(company,number)
> OK
> Time: 0.085s
    
INSERT INTO company VALUES('company1','England'),('company2','America')
> Affected rows: 2
> Time: 0.073s
    
INSERT INTO airplane VALUES('company1',100),('company1',200),('company2',100),('company2',200)
> Affected rows: 4
> Time: 0.074s
    
SELECT * FROM airplane JOIN company ON airplane.company = company.name
> OK
> Time: 0.001s

INSERT OR IGNORE INTO airplane VALUES('company1',100),('company1',500)
> Affected rows: 1
> Time: 0.082s

SELECT * FROM airplane JOIN company ON airplane.company = company.name
> OK
> Time: 0s

【讨论】:

以上是关于是否可以在数据库中为外键创建唯一的字段?的主要内容,如果未能解决你的问题,请参考以下文章

在 Laravel 中如何通过 Model 知道表列是不是为外键?

在 EF 4 上映射外键而不创建新的 Edmx 文件

Django 会自动为外键列生成索引吗?

如何在laravel中将主键本身设置为外键?

EF5.X Code First表关联与延迟加载

数据库中,主键是不能重复,唯一的,请问外键是否也不能重复的??