在 SQL 中,当复合主键成为外键时应该如何被其它表引用

文章探讨了SQL中复合主键情况下,如何正确设置外键引用,特别是在User、Folder和Contact三个表之间的关系。强调了当复合主键成为外键时,必须引用整个复合键,而不是单独的部分。不同数据库如MySQL和SQLite对此有不同的处理方式,但重要的是确保数据库逻辑的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

  当研究一个问题慢慢深入时,一个看起来简单的问题也暗藏玄机。在 SQL 中,主键成为外键这是一个很平常的问题,乍一看没啥值得注意的。但如果这个主键是一种复合主键,而另一个表又引用这个键作为它的复合主键,问题就会变得复杂。

  这里为了便于说明,简单抽象出这样一个情景。数据库中有很多用户(User),每个用户有他的好友分组(Folder),每个分组下面有该用户的好友(Contact)。下面看看应该如何建表。

  • User 表建表示例代码如下:

    CREATE TABLE User (
        id VARCHAR(64) NOT NULL,
        name VARCHAR(64) NOT NULL,
        # ...为了简化说明,此表省略其它字段...
        PRIMARY KEY (id)
    );
    
    
  • Folder 表建表示例代码如下:

    CREATE TABLE Folder (
        id VARCHAR(64) NOT NULL,
        userId VARCHAR(64) NOT NULL,
        name VARCHAR(64) NOT NULL,
        # ...为了简化说明,此表省略其它字段...
        PRIMARY KEY (userId, id),
        # 因为上面的是复合主键,所以自动创建的是联合索引,而其它表的外键引用需要的是单个索引
        INDEX idIndex (id),
        FOREIGN KEY (userId) REFERENCES User (id)
    );
    
    
  • Contact 表建表示例代码如下:

    CREATE TABLE Contact (
        id VARCHAR(64) NOT NULL,
        # 表示此联系人属于谁的好友
        userId VARCHAR(64) NOT NULL,
        # 表示此联系人对应 User 中的 id
        linkedUserId VARCHAR(64) NOT NULL,
        folderId VARCHAR(64) NOT NULL,
        # ...为了简化说明,此表省略其它字段...
        PRIMARY KEY (userId, id),
        # 因为上面的是复合主键,所以自动创建的是联合索引,而其它表的外键引用需要的是单个索引
        INDEX idIndex (id),
        # 同一个用户,不能拥有两个相同 ID 的 Contact
        UNIQUE (userId, linkedUserId),
        # 当复合主键成为外键时,必须整个复合主键一起作为外键,不能只引用复合主键其中的某个属性
        FOREIGN KEY (userId) REFERENCES Folder (userId),
        FOREIGN KEY (folderId) REFERENCES Folder (id),
        FOREIGN KEY (linkedUserId) REFERENCES User (id)
    );
    
  • 建表示意图如下:

    在这里插入图片描述

    在这里插入图片描述

  笔者已经将正确的建表在上述给出了,但问题其实不简单。注意看,表 Contact 引用了两个表的外键:表 User 和 表 Folder。而很奇妙的是,表 User、Folder 和 Contact 都有一个形同“userId”的字段,而且,它们的这个字段的意义是相同的。那么,表 Contact 中的 userId 究竟是引用表 User 中的 id 呢?还是引用表 Folder 中的 userId 呢?还是随便选一个都可以呢?

  答案是必须引用表 Folder 中的 userId,而不能引用表 User 中的 id。原因是,当复合主键成为外键时,必须整个复合主键一起作为外键,不能只引用复合主键其中的某个属性

  注意看,表 Contact 的属性 folderId 已经引用了表 Folder 的属性 id 了,但表 Folder 的主键是 (id,userId)。在这个情形下,表 Contact 必须也同时引用表 Folder 关于这个复合主键的其它属性。而正好表 Contact 有一个属性 userId,所以它必须引用表 Folder 中的 userId。

  可是,表 Contact 不是也和表 User 有引用啊,为什么没有受到表 User 的约束呢?因为 User 的主键不是复合主键,而且表 Contact 已经通过属性 linkedUserId 引用了表 User 的主键 id,因此不需要强制引用 User 的其它属性。

  当然,这只是 SQL 上的逻辑,而 SQL 只是一套标准,各个数据库的服务提供商对 SQL 的支持和实现因厂商而异。在 MySQL 中,如果建表时错误地将上述表 Contact 中的 userId 引用了表 User 中的 id,MySQL 并不会抛出错误,因为 MySQL 关于这方面不太严谨。但如果使用的是 SQLite,SQLite 将会在建表时就抛出错误。但不管各厂商的支持如何,将应该保证数据库建表的逻辑是正确的。

<think>嗯,用户想了解如何在MySQL中创建主键SQL语句。好的,我需要先回忆一下相关的知识,然后根据提供的引用内容来整理答案。 首先,主键是用于唯一标识中每一行的列或列组合,而则用于建立两个之间的关联。根据引用[1],创建主键可以在创建定义,或者在已有上添加。比如在CREATE TABLE的使用PRIMARY KEY关字,或者在现有上通过ALTER TABLE添加。比如引用[3]中提到,使用ALTER TABLE users ADD PRIMARY KEY (user_id)来添加主键。 然后是引用[4]中提到,可以在创建定义FOREIGN KEY,并指定REFERENCES父的列。或者在已有使用ALTER TABLE添加约束。例如,在创建子,FOREIGN KEY (parent_id) REFERENCES parent_table(id)。还有引用[5]提到,如果原有主键需要删除,可以使用ALTER TABLE DROP PRIMARY KEY,然后再添加新的主键。 用户可能需要具体的示例,所以应该给出创建和修改SQL语句。比如在创建如何设置主键,包括单字段主键复合主键部分也需要说明在创建和之后如何添加,并且注意约束的选项,如ON DELETE和ON UPDATE。 还需要注意自增属性,主键通常和自增一起使用,比如使用AUTO_INCREMENT,这在引用[1]和[4]中都有提到。例如,id INT AUTO_INCREMENT PRIMARY KEY。 用户可能遇到的问题包括如何删除原有主键,这在引用[5]中提到,需要用ALTER TABLE DROP PRIMARY KEY。但需要注意,如果原有主键是自增的,可能需要先修改列属性,再删除主键,不过这可能超出当前问题的范围,但可以简要提及。 另需要确保父的列存在索引,并且存储引擎是InnoDB,否则可能不支持约束。这点可能在引用中没有提到,但根据MySQL的知识,需要补充说明,以避免用户在使用出现错误。 总结一下,回答的结构应该分为创建主键和创建两部分,每部分再分为创建和修改已有两种情况,并给出示例。同,注意事项也需要提到,比如存储引擎、索引、约束条件等。 现在需要检查是否有遗漏的引用内容,比如引用[2]和[3]可能提供其他信息,但根据内容,引用[3]提到添加主键的方法,引用[4]和[5]涉及的创建和修改。需要确保所有相关的操作都有覆盖,比如删除主键的步骤,但用户的问题主要是创建,所以可能不需要详细展开删除部分,除非必要。 最后,生成相关问题,可能需要包括主键的区别、如何删除、性能影响、修改约束等,这些可以帮助用户进一步学习。</think>### MySQL主键创建方法 #### 一、主键创建方法 主键用于唯一标识中的每一条记录,**创建方式分为建定义和后期添加**: 1. **建定义主键** - 单字段主键: ```sql CREATE TABLE users ( user_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL ); ``` - 复合主键(多字段组合): ```sql CREATE TABLE orders ( order_id INT, product_id INT, PRIMARY KEY (order_id, product_id) ); ``` 2. **已有添加主键** ```sql ALTER TABLE employees ADD PRIMARY KEY (emp_id); ``` **注意**:若已有主键需先删除原主键[^5]: ```sql ALTER TABLE jingyan DROP PRIMARY KEY; ``` #### 二、创建方法 用于关联两个的数据,**需确保父存在对应主键使用InnoDB引擎**: 1. **建定义** ```sql CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE ); ``` 2. **已有添加** ```sql ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(user_id); ``` #### 三、核心注意事项 1. **自增属性**:主键常配合`AUTO_INCREMENT`实现自动编号[^1] 2. **约束条件**: - `ON DELETE CASCADE`:父记录删除自动删除子关联记录 - `ON UPDATE CASCADE`:父主键更新同步更新子 3. **引擎限制**:仅InnoDB引擎支持约束[^4] 4. **索引要求**:字段会自动创建索引以提高查询效率 $$ \text{关系公式:} \boxed{\text{子.字段} \subseteq \text{父.主键字段}} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值