05.MySQL表的约束

MySQL表的约束

MySQL表的约束
1. 空属性
2. 默认值
3. 列描述
4. zerofill
5. 主键
6. 自增长
7. 唯一键
8. 外键
9. 综合案例


MySQL表的约束

说到 MySQL 表的约束,这绝对是数据库设计里绕不开的一个话题。很多人一提“约束”,第一反应可能是字段的数据类型,比如 INT 不能存字符串,VARCHAR 有长度限制。这当然是最基本的一层限制,但光靠这些,远远不够。就像你家门口装了密码锁,结果厨房窗户忘关了——防得了一部分,还是会漏。

这时候就得靠表级的各种约束来兜底,从业务逻辑的角度出发,帮你把不合理的数据挡在门外。

举个栗子🌰:假设你建了一个用户表,里面有个“年龄”字段,类型是 INT。这时候要是有人输入个 -100 或者 999999999 岁,数据库是能存下没错,但你一看就知道这不靠谱。这种数值范围的控制,其实属于“检查约束(CHECK)”。不过早期的 MySQL 并不支持这玩意儿(8.0 开始才支持),所以我们今天先不展开这个,重点聊聊其他你在开发里经常用到的约束类型。

这篇文章我们就来盘讲讲这些实用技能:空属性、默认值、列描述、zerofill、主键、自增长、唯一键、外键。最后还会用一个综合案例,把这些知识点串起来,帮你真正掌握这些“看起来简单但常常出问题”的细节。

1. 空属性

1.1 NULL和NOT NULL的相爱相杀

数据库里默认所有字段都是允许为空的(NULL),就像你家的冰箱——什么都能塞进去。但实际开发中,咱们得学会"断舍离",把不该空的字段锁死。为啥?因为空值是个麻烦精!它参与运算时会直接让结果变NULL,就像往火锅里倒可乐——全毁了。

举个例子🌰:假设你统计销售额,某个订单金额是NULL,那么SELECT SUM(price)的结果也会是NULL。这时候老板要是问"今天赚了多少",你只能尴尬地回一句:“不知道啊,有笔订单金额没填…”

所以遇到必填项,一定要用NOT NULL。比如班级表的教室字段:

CREATE TABLE class(class_name VARCHAR(20) NOT NULL,classroom VARCHAR(20) NOT NULL
);

这时候插入数据时,这两个字段就必须填,否则MySQL会直接报错:

INSERT INTO class(class_name) VALUES('三年二班');
-- 报错:Field 'classroom' doesn't have a default value

1.2 NULL的隐藏陷阱

很多人以为NULL就是"没有值",其实它更像"未知值"。比如两个NULL比较时,既不等于也不等于不,永远返回UNKNOWN。这会导致查询时出现意想不到的结果:

SELECT * FROM users WHERE email = NULL; -- 查不到任何数据
SELECT * FROM users WHERE email IS NULL; -- 才能查到空邮箱用户

1.3 NOT NULL的进阶玩法

有时候我们会给字段设置默认值,这时候再加NOT NULL就显得多余了。比如:

CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT,gender ENUM('男','女') DEFAULT '男' NOT NULL
);

这里的NOT NULL其实可以省略,因为默认值已经保证了字段不为空。但要注意默认值的类型匹配,比如用字符串’0’当默认值,字段类型却是INT的话,可能会触发隐式转换。


2. 默认值

2.1 DEFAULT的魔法时刻

默认值就像自动售货机——当你不投币时,它自己吐出预设的商品。比如用户表的性别字段:

CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(20),gender ENUM('男','女') DEFAULT '男'
);

这时候插入数据时,如果不指定gender字段,就会自动填充"男":

INSERT INTO user(name) VALUES('张三');
-- 实际插入的是('张三', '男')

2.2 默认值的进阶套路

默认值不仅能用常量,还能用表达式(MySQL 8.0+支持):

CREATE TABLE orders(order_id INT PRIMARY KEY AUTO_INCREMENT,create_time DATETIME DEFAULT NOW(),expire_time DATETIME DEFAULT (NOW() + INTERVAL 7 DAY)
);

不过要注意,同一个字段不能同时有默认值和NOT NULL约束(除非默认值明确指定),否则会触发冲突。

2.3 默认值的隐藏彩蛋

对于日期时间类型字段,默认值有特殊规则:

  • DATETIME默认值只能是常量,不能用函数
  • TIMESTAMP会自动设置当前时间作为默认值(如果未显式指定)

比如:

CREATE TABLE test(id INT PRIMARY KEY AUTO_INCREMENT,dt DATETIME,           -- 默认值为NULLts TIMESTAMP           -- 默认值自动变为CURRENT_TIMESTAMP
);

3. 列描述

3.1 COMMENT的文艺复兴

列描述就像给数据库字段写小纸条,方便后来人看懂你的设计思路。比如用户表:

CREATE TABLE user(id INT PRIMARY KEY COMMENT '用户ID',name VARCHAR(20) COMMENT '用户真实姓名',nickname VARCHAR(20) COMMENT '用户昵称(可为空)'
);

通过SHOW CREATE TABLE user;就能看到这些注释:

CREATE TABLE `user` (`id` int(11) NOT NULL COMMENT '用户ID',`name` varchar(20) NOT NULL COMMENT '用户真实姓名',`nickname` varchar(20) DEFAULT NULL COMMENT '用户昵称(可为空)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.2 描述的最佳实践

  1. 业务含义:说明字段的业务逻辑,比如status TINYINT COMMENT '状态:1-待支付 2-已支付 3-已发货'
  2. 数据来源:标明数据是怎么来的,比如score DECIMAL(5,2) COMMENT '根据考试成绩自动同步'
  3. 变更记录:重要字段的变更历史,比如address VARCHAR(255) COMMENT '2022.3新增,原地址字段已废弃'

3.3 描述的隐藏功能

结合INFORMATION_SCHEMA.COLUMNS表,可以实现自动化文档生成:

SELECT COLUMN_NAME, COLUMN_COMMENT 
FROM information_schema.columns
WHERE TABLE_SCHEMA='your_db' AND TABLE_NAME='user';

4. zerofill

4.1 数字显示的艺术

zerofill就像给数字穿上西装打上领带——让它看起来更正式。比如订单编号:

CREATE TABLE orders(order_id INT(6) ZEROFILL PRIMARY KEY AUTO_INCREMENT
);

插入数据时:

INSERT INTO orders() VALUES();
-- 实际显示order_id为000001

注意这里的INT(6)不是指6位数字,而是显示宽度。底层存储还是标准的INT类型(4字节)。

4.2 zerofill的连带效应

使用zerofill会自动触发UNSIGNED属性:

CREATE TABLE test(a INT(5) ZEROFILL);
-- 实际相当于 INT(5) UNSIGNED ZEROFILL

所以字段只能存储正数,负数插入会变成0。

4.3 使用场景分析

适合需要固定显示位数的业务场景:

  • 学号:202301010001(年份+学院代码+序号)
  • 发票号码:0000123456
  • 产品编号:P000001

但要注意,这种格式化更适合前端处理,数据库层面保持纯粹数字更利于计算。


5. 主键

5.1 数据的身份证

主键就像每个人的身份证号码——必须唯一且不能为空。创建学生表:

CREATE TABLE student(stu_id CHAR(10) PRIMARY KEY COMMENT '学号(唯一标识)',name VARCHAR(20)
);

这时候插入重复学号会直接报错:

INSERT INTO student VALUES('2023010101','张三');
INSERT INTO student VALUES('2023010101','李四');
-- 报错:Duplicate entry '2023010101' for key 'PRIMARY'

5.2 主键的进阶操作

  1. 删除主键

    ALTER TABLE student DROP PRIMARY KEY;
    

    注意:如果该列有自增属性,需要先删除自增

  2. 修改主键

    ALTER TABLE student MODIFY stu_id CHAR(12);
    -- 修改字段类型不影响主键约束
    
  3. 复合主键

    CREATE TABLE cart(user_id INT,product_id INT,quantity INT,PRIMARY KEY(user_id, product_id)
    );
    

    这时候单个字段可以重复,但组合必须唯一

5.3 主键选择的玄学

  1. 自增主键 vs 业务主键
    自增主键(如AUTO_INCREMENT)更利于索引性能,业务主键(如身份证号)更直观,需要根据场景权衡

  2. UUID的另类玩法

    CREATE TABLE orders(order_id CHAR(36) PRIMARY KEY DEFAULT UUID(),...
    );
    

    适合分布式系统,但会占用更多存储空间


6. 自增长

6.1 AUTO_INCREMENT的魔法

自增字段就像自动步枪——每次扣动扳机都会自动装弹。创建文章表:

CREATE TABLE articles(article_id INT AUTO_INCREMENT PRIMARY KEY,title VARCHAR(100)
);

插入数据时:

INSERT INTO articles(title) VALUES('MySQL约束详解');
-- article_id自动分配为1
INSERT INTO articles(title) VALUES('性能优化技巧');
-- article_id自动分配为2

6.2 自增的隐藏技巧

  1. 指定初始值

    CREATE TABLE users(id INT AUTO_INCREMENT PRIMARY KEY
    ) AUTO_INCREMENT = 1000;
    
  2. 跳增现象
    如果插入失败或事务回滚,自增值不会回退。比如插入100后失败,下一个值会是101而不是100

  3. 手动赋值

    INSERT INTO articles(article_id, title) VALUES(10000, '特别推荐');
    -- 下次自增值从10001开始
    

6.3 自增的性能考量

  1. 并发问题
    InnoDB引擎使用互斥锁来确保自增的连续性,在高并发场景可能成为瓶颈

  2. 缓存配置
    innodb_autoinc_lock_mode参数影响自增行为,需要根据业务调整

  3. 迁移风险
    导出导入数据时,记得检查自增字段的当前值:

    SHOW TABLE STATUS LIKE 'articles';
    -- 查看Auto_increment列
    

7. 唯一键

7.1 除主键外的唯一保障

唯一键就像班级里的学号和电话号码——都可以唯一标识学生,但只能选一个当主键。创建用户表:

CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(20) UNIQUE,email VARCHAR(50) UNIQUE
);

这时候用户名和邮箱都必须唯一:

INSERT INTO user(username, email) 
VALUES('zhangsan', 'zhangsan@example.com');-- 插入相同用户名会失败
INSERT INTO user(username, email) 
VALUES('zhangsan', 'zs@example.com');
-- 报错:Duplicate entry 'zhangsan' for key 'username'

7.2 唯一键的骚操作

  1. 复合唯一键

    CREATE TABLE exam(student_id INT,subject VARCHAR(20),score INT,UNIQUE(student_id, subject)
    );
    

    保证同一个学生同一科目只有一条记录

  2. 空值处理
    唯一键允许有多个NULL值(这与主键不同):

    INSERT INTO user(username) VALUES(NULL);
    INSERT INTO user(username) VALUES(NULL);
    -- 两条记录都能成功插入
    
  3. 延迟约束
    在事务中,唯一约束检查可以延迟到提交时:

    SET CONSTRAINTS ALL DEFERRED;
    -- 需要配合支持的存储引擎
    

7.3 唯一键的优化技巧

  1. 前缀索引
    对长字符串字段,可以只索引前缀:

    CREATE TABLE products(id INT PRIMARY KEY,product_code VARCHAR(100) UNIQUE
    );
    -- 改为
    CREATE UNIQUE INDEX idx_code ON products(product_code(20));
    
  2. 合并索引
    如果某个查询经常同时用到username和email,可以创建联合唯一索引:

    CREATE UNIQUE INDEX idx_user_email ON user(username, email);
    

8. 外键

8.1 表之间的羁绊

外键就像亲子关系——孩子必须知道自己爹是谁。创建订单表:

CREATE TABLE orders(order_id INT PRIMARY KEY AUTO_INCREMENT,user_id INT,FOREIGN KEY(user_id) REFERENCES users(id)
);

这时候插入订单时:

-- 假设users表中没有id=100的用户
INSERT INTO orders(user_id) VALUES(100);
-- 报错:Cannot add or update a child row

8.2 外键的连环反应

  1. 级联更新
    当父表主键变更时,子表自动更新:

    CREATE TABLE orders(order_id INT PRIMARY KEY AUTO_INCREMENT,user_id INT,FOREIGN KEY(user_id) REFERENCES users(id) ON UPDATE CASCADE
    );
    
  2. 级联删除
    删除父表记录时,自动删除子表关联数据:

    FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
    
  3. 置空操作
    删除父表记录时,将子表外键字段设为NULL:

    FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL
    -- 注意:字段必须允许NULL
    

8.3 外键的性能博弈

  1. 锁机制
    修改父表主键时,会锁定子表相关记录,可能引发死锁

  2. 批量导入
    导入大量数据时,建议先关闭外键检查:

    SET FOREIGN_KEY_CHECKS=0;
    -- 执行导入操作
    SET FOREIGN_KEY_CHECKS=1;
    
  3. 索引优化
    外键字段必须有索引,否则会影响性能:

    CREATE INDEX idx_user ON orders(user_id);
    -- 如果创建外键时未自动创建
    

9. 综合案例

9.1 商城系统的数据设计

让我们来设计一个简单的电商系统,包含三个核心表:

9.1.1 商品表(goods)
CREATE TABLE goods(goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID',goods_name VARCHAR(100) NOT NULL COMMENT '商品名称',unitprice DECIMAL(10,2) NOT NULL COMMENT '单价',category VARCHAR(50) COMMENT '分类',provider VARCHAR(100) COMMENT '供应商',stock INT DEFAULT 0 COMMENT '库存',INDEX idx_category(category)
) ENGINE=InnoDB;
9.1.2 客户表(customer)
CREATE TABLE customer(customer_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '客户ID',name VARCHAR(20) NOT NULL COMMENT '姓名',address VARCHAR(200) COMMENT '住址',email VARCHAR(50) UNIQUE COMMENT '邮箱',sex ENUM('男','女') COMMENT '性别',card_id CHAR(18) UNIQUE COMMENT '身份证',reg_time DATETIME DEFAULT NOW() COMMENT '注册时间'
) ENGINE=InnoDB;
9.1.3 购买表(purchase)
CREATE TABLE purchase(order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单号',customer_id INT NOT NULL COMMENT '客户ID',goods_id INT NOT NULL COMMENT '商品ID',nums INT NOT NULL DEFAULT 1 COMMENT '购买数量',order_time DATETIME DEFAULT NOW(),FOREIGN KEY(customer_id) REFERENCES customer(customer_id),FOREIGN KEY(goods_id) REFERENCES goods(goods_id),INDEX idx_time(order_time)
) ENGINE=InnoDB;

9.2 设计亮点解析

  1. 约束组合拳

    • 客户姓名NOT NULL保证必填
    • 邮箱和身份证UNIQUE防止重复
    • 性别用ENUM限制取值范围
    • 购买数量DEFAULT 1避免零值
  2. 性能优化

    • 商品分类添加索引
    • 订单时间建立索引方便按时间查询
  3. 数据完整性

    • 外键约束确保订单中的客户和商品真实存在
    • 级联操作可自行添加(根据业务需求)

9.3 扩展思考

  1. 库存扣减
    在购买表插入记录时,需要更新商品表库存。可以通过触发器实现:

    DELIMITER //
    CREATE TRIGGER after_purchase_insert 
    AFTER INSERT ON purchase
    FOR EACH ROW
    BEGINUPDATE goods SET stock = stock - NEW.nums WHERE goods_id = NEW.goods_id;
    END//
    DELIMITER ;
    
  2. 订单状态
    可以添加status字段表示订单状态(待支付、已发货等),配合外键关联状态字典表

  3. 分区策略
    对于大规模数据,可以按订单时间做分区:

    CREATE TABLE purchase(...) 
    PARTITION BY RANGE (YEAR(order_time)) (PARTITION p2022 VALUES LESS THAN (2023),PARTITION p2023 VALUES LESS THAN (2024)
    );
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83612.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web3如何重塑数据隐私的未来

在这个信息爆炸的时代,数据隐私已成为我们不得不面对的严峻问题。Web3,作为下一代互联网的代表,以其去中心化、用户主权和数据安全等特点,正在重塑数据隐私的未来。它不仅仅是技术的革新,更是对个人隐私保护理念的一次…

网络编程之TCP编程

基于 C/S &#xff1a;客户端&#xff08;client&#xff09;/服务器端&#xff08;server&#xff09; 1.流程 2. 函数接口 所有函数所需头文件&#xff1a; #include <sys/types.h> #include <sys/socket.h> 系统定义好了用来存储网络信息的结构体 ipv4通信使…

各个布局的区别以及示例

各个布局的区别以及示例 在前端开发中&#xff0c;常见的布局方式主要有以下几种&#xff0c;每种布局都有其适用场景和特点&#xff1a; 1. 普通文档流&#xff08;Normal Flow&#xff09; 特点&#xff1a;默认布局方式&#xff0c;元素按照HTML顺序依次排列。适用场景&am…

性能优化 - 理论篇:常见指标及切入点

文章目录 引言一、 Java 性能优化的核心思路二、为什么要度量&#xff1f;三、常用性能衡量指标详解3.1 吞吐量与响应速度3.2 响应时间的具体度量&#xff1a;平均响应时间与百分位数3.3 并发量3.4 秒开率&#xff08;页面秒开&#xff09;3.5 正确性&#xff08;功能可用性&am…

第2讲、Odoo深度介绍:开源ERP的领先者

一、Odoo深度介绍&#xff1a;开源ERP的领先者 Odoo&#xff0c;其前身为OpenERP&#xff0c;是一款在全球范围内广受欢迎的开源企业管理软件套件。它不仅仅是一个ERP系统&#xff0c;更是一个集成了客户关系管理&#xff08;CRM&#xff09;、电子商务、网站构建、项目管理、…

Telerik生态整合:Kendo UI for Angular组件在WinForms应用中的深度嵌入(一)

Telerik DevCraft包含一个完整的产品栈来构建您下一个Web、移动和桌面应用程序。它使用HTML和每个.NET平台的UI库&#xff0c;加快开发速度。Telerik DevCraft提供完整的工具箱&#xff0c;用于构建现代和面向未来的业务应用程序&#xff0c;目前提供UI for ASP.NET MVC、Kendo…

2025年6月4日收获

Authorization Authorization是一种通用的、标准化的权限控制和认证的通用框架&#xff0c;它能够使跨系统和跨域的身份验证和授权管理更容易&#xff0c;使不同应用程序之间能够更轻松地实现单点登录&#xff08;SSO&#xff09;、用户身份验证和授权控制等。 在前端使用 axi…

实时数据湖架构设计:从批处理到流处理的企业数据战略升级

企业数据处理架构正在经历一场深刻的变革。从最初的数据仓库T1批处理模式&#xff0c;到如今的实时流处理架构&#xff0c;这一演进过程反映了业务对数据时效性要求的不断提升。 文章目录 第一章&#xff1a;数据湖演进历程与现状分析 第二章&#xff1a;实时数据湖核心架构剖…

iptables实战案例

目录 一、实验拓扑 二、网络规划 三、实验要求 四、环境准备 1.firewall &#xff08;1&#xff09;配置防火墙各大网卡IP并禁用 firewall和selinux &#xff08;2&#xff09;打开firewall路由转发 2.PC1&#xff08;内网&#xff09; &#xff08;1&#xff09;配置防…

macOS 连接 Docker 运行 postgres,使用navicat添加并关联数据库

下载 docker注册一个账号&#xff0c;登录 Docker创建 docke r文件 mkdir -p ~/.docker && touch ~/.docker/daemon.json写入配置&#xff08;全量替换&#xff09; {"builder": {"gc": {"defaultKeepStorage": "20GB",&quo…

docker离线镜像下载

背景介绍 在某些网络受限的环境中&#xff0c;直接从Docker Hub或其他在线仓库拉取镜像可能会遇到困难。为了在这种情况下也能顺利使用Docker镜像&#xff0c;我们可以提前下载好所需的镜像&#xff0c;并通过离线方式分发和使用。 当前镜像有&#xff1a;python-3.8-slim.ta…

Android 3D球形水平圆形旋转,旋转动态更换图片

看效果图 1、事件监听类 OnItemClickListener&#xff1a;3D旋转视图项点击监听器接口 public interface OnItemClickListener {/*** 当旋转视图中的项被点击时调用** param view 被点击的视图对象* param position 被点击项在旋转视图中的位置索引&#xff08;从0开始&a…

48V带极性反接保护-差共模浪涌防护方案

在工业自动化&#xff08;电动机驱动 / 工业机器人&#xff09;、交通基础设施&#xff08;充电桩 / 车载电子&#xff09;、安防系统&#xff08;监控摄像头 / 门禁&#xff09;、储能设备&#xff08;BMS / 离网控制器&#xff09;等领域&#xff0c;DC48V 电源因安全特低电压…

CentOS在vmware局域网内搭建DHCP服务器【踩坑记录】

1. 重新设置环境 配置dhcp服务踩了不少坑&#xff0c;这里重头搭建记录一下&#xff1a; 1.1 centos 网卡还原 如果之前搭了乱七八糟的环境&#xff0c;导致NAT模式也没法上网&#xff0c;这里重新还原 我们需要在NAT模式下联网&#xff0c;下载DHCP服务 先把centos的网卡还…

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…

Web3时代的数据保护挑战与应对策略

随着互联网技术的飞速发展&#xff0c;我们正步入Web3时代&#xff0c;这是一个以去中心化、用户主权和数据隐私为核心的新时代。然而&#xff0c;Web3时代也带来了前所未有的数据保护挑战。本文将探讨这些挑战&#xff0c;并提出相应的应对策略。 数据隐私挑战 在Web3时代&a…

从零打造算法题刷题助手:Agent搭建保姆级攻略

我用Trae 做了一个有意思的Agent 「大厂机试助手」。 点击 https://s.trae.com.cn/a/d2a596 立即复刻&#xff0c;一起来玩吧&#xff01; Agent 简介 Agent名称为大厂机试助手&#xff0c;主要功能有以下三点。 解题&#xff1a; 根据用户给出的题目给出具体的解题思路引导做…

【JavaWeb】MVC三层架构

MVC三层架构 MVC 是什么&#xff1f;三层架构的组成&#xff08;View、Controller、Model&#xff09;各层职责划分示例说明面试高频问题与参考答案 MVC&#xff08;Model-View-Controller&#xff09;是一种经典的软件设计模式&#xff0c;广泛应用于 Web 应用开发中&#xf…

嵌入式分析利器:DuckDB与SqlSugar实战

​ 一、DuckDB 的核心特性与适用场景 DuckDB 是一款 嵌入式分析型数据库&#xff08;OLAP&#xff09; &#xff0c;专为高效查询设计&#xff0c;主要特点包括&#xff1a; 列式存储与向量化引擎 数据按列存储&#xff0c;提升聚合统计效率&#xff08;如 SUM/AVG&#xf…

React---day6、7

6、组件之间进行数据传递 **6.1 父传子&#xff1a;**props传递属性 父组件&#xff1a; <div><ChildCpn name"蒋乙菥" age"18" height"1,88" /> </div>子组件&#xff1a; export class ChildCpn extends React.Component…