一、表的约束
表的约束是数据库中用于确保数据完整性和一致性的规则,主要包括主键约束、外键约束、唯一约束等。
1.1、非空约束
NOT NULL 约束字段的值不能为空,确保列不能为空值(NULL),强制插入数据时指定列必须有值,否则报错。
sql
-- 创建表时添加 NOT NULL 约束
CREATE TABLE students (
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
age INT,
address VARCHAR(100)
);
-- 插入数据测试
INSERT INTO students (id, name, age, address) VALUES (1, '张三',
20, '北京');
-- 下面这条会报错,因为 id 和 name 不能为空
INSERT INTO students (age, address) VALUES (20, '上海');

1.2、唯一约束
UNIQUE约束字段中的数据不允许重复,确保列中的所有值都是唯一的(不允许重复,但是允许有一个 NULL 值(MySQL 特 性)),一个表可以有多个 UNIQUE 约束。
sql
CREATE TABLE users (
id INT NOT NULL,
username VARCHAR(50) UNIQUE,
email VARCHAR(100) UNIQUE,
password VARCHAR(255)
);
-- 插入数据测试
INSERT INTO users (id, username, email, password)
VALUES (1, 'user1', 'user1@example.com', 'pass123');
INSERT INTO users (id, username, email, password)
VALUES (2, 'user2', 'user2@example.com', 'pass456');
-- 下面这条会报错,因为用户名已存在
INSERT INTO users (id, username, email, password)
VALUES (3, 'user1', 'user3@example.com', 'pass789'); -- 错误:用户
名重复

1.3、主键约束
PRIMARY KEY 约束字段中的数据唯一且不允许重复(一张表只能有一个主键)
组合主键:primary key pkey(sn,name);
默认值:default xxx;
注意:unique和primary key可以针对多个字段同时设置唯一或主键
sql
-- 创建表时添加 PRIMARY KEY 约束
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2),
category VARCHAR(50)
);
-- 插入数据测试
INSERT INTO products (product_id, product_name, price, category)
VALUES (1, '笔记本电脑', 5999.00, '电子产品');
-- 下面这两条都会报错
INSERT INTO products (product_id, product_name, price, category)
VALUES (2, '手机', 2999.00, '电子产品');
-- 错误:主键重复
INSERT INTO products (product_name, price, category)
VALUES ('平板电脑', 1999.00, '电子产品'); -- 错误:主键不能为空

1.4、外键约束
外键约束:约束表1中指定字段的数据,必须在表2中存在
创建两个表----部门表departments(dept_id,dept_name,location)和员工表empolyees(epm_id,emp_name,dept_id,salary)对dept_id进行外键约束
sql
CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL,
location VARCHAR(100)
);
-- 创建子表(从表),包含外键约束
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INT,
salary DECIMAL(10,2),
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
--向两个表插入数据
INSERT INTO departments (dept_id, dept_name, location)
VALUES
(1, '技术部', '北京'),
(2, '销售部', '上海'),
(3, '人事部', '广州');
INSERT INTO employees (emp_id, emp_name, dept_id, salary)
VALUES
(101, '李四', 1, 8000.00),
(102, '王五', 2, 6000.00);

插入数据成功,然后验证外键约束:在员工表中插入,dept_id=5,则会报错,此记录不存在
sql
INSERT INTO employees (emp_id, emp_name, dept_id, salary)
VALUES (103, '赵六', 5, 7000.00); -- 错误:外键约束违反

1.5、检查约束
检查约束(CHECK)是一种用于限制数据库表中列或多列取值范围的约束,通过在 CREATE TABLE 或 ALTER TABLE 中定义逻辑表达式来确保数据满足指定条件,如 age BETWEEN 15 AND 40 或 P_Id > 0。
CHEKC约束:限制在这里面范围
**额外补充:**COMMENT:表示可以保注释,就是在代码里面写注释的时候,在查看的时候,会显示注释。
sql
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
age INT CHECK (age >= 18 AND age <= 65),
salary DECIMAL(10,2) CHECK (salary >= 0)
);
--插入数据测试
INSERT INTO employees (emp_id, emp_name, age, salary)
VALUES (1, '孙七', 25, 5000.00);
INSERT INTO employees (emp_id, emp_name, age, salary)
VALUES (2, '周八', 17, 4000.00);

1.6、默认约束
默认约束(DEFAULT)用于在数据库表的某列未提供值时自动填入预设默认值,例如在 MySQL 中可通过 字段名 数据类型 DEFAULT 默认值 设置,或用 ALTER TABLE ... ALTER COLUMN ... SET DEFAULT 添加默认值。
sql
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_name VARCHAR(50) NOT NULL,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) DEFAULT '待处理',
total_amount DECIMAL(10,2) DEFAULT 0.00
);
--插入数据
INSERT INTO orders (order_id, customer_name)
VALUES (1, '吴九');-- order_date 会自动设为当前日期,status 为'待处理',total_amount 为
0.00
INSERT INTO orders (order_id, customer_name, order_date, status,
total_amount)
VALUES (2, '郑十', '2024-01-20', '已发货', 1500.00);

1.7、自增扩展
自增扩展:针对整型字段设置的一个扩展属性,auto_increment,用于生成唯一的标识符,通常用于主键。
sql
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50)
);
-- 设置起始值和步长
ALTER TABLE users AUTO_INCREMENT = 1000;

sql
--测试数据插入例子
INSERT INTO users(username)
VALUES ('张三');
INSERT INTO users(username)
VALUES ('李四');
INSERT INTO users(username)
VALUES ('王五');
INSERT INTO users(username)
VALUES ('小明');
INSERT INTO users(username)
VALUES ('小红');
INSERT INTO users(username)
VALUES ('小芳');

1.8、生成列
在 SQL 数据库中, 生成列(Generated Column)是指由表中其他字段计算得到的列 ,因此也称为计算列(Computed Column)。 生成列存在两种类型:存储(stored)生成列和虚拟(virtual)生成列。 存储生成列和普通列类似,在插入或者更新数据时自动计算并且存储该列的值,需要占用存储空间;虚拟生成列不需要占用存储空间,只在读取时进行计算。STORED 让写入变慢、查询变快;VIRTUAL 让写入变快、查询变慢。

sql
CREATE TABLE orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
qty INT NOT NULL DEFAULT 1,
price DECIMAL(10,2) NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,--时间戳更新,随时有新的时间
total DECIMAL(12,2) AS (qty * price) STORED,
full_name VARCHAR(101) AS (CONCAT(first_name,' ',last_name)) VIRTUAL,
INDEX idx_total (total),
INDEX idx_full_name (full_name),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB;
INSERT INTO orders (user_id, qty, price, first_name, last_name)
VALUES (1, 2, 100.00, 'San', 'Zhang'),
(2, 3, 50.00, 'Si', 'Li');

sql
-- 也正确:显式写 DEFAULT 占位
INSERT INTO orders (user_id, qty, price, first_name, last_name, total)
VALUES (3, 1, 20.00, 'Wu', 'Wang', DEFAULT);

sql
-- 错误:手动赋值直接报错
INSERT INTO orders (user_id, qty, price, first_name, last_name, total)
VALUES (4, 1, 20.00, 'Liu', 'Zhao', 999);
-- ERROR 3105 (HY000): The value specified for generated column 'total' in table 'orders' is not allowed.

二、ER图
ER图(实体-关系图)的主要作用是用于数据库设计,通过可视化方式明确实体、属性及其关系,从而指导数据建模、减少冗余并帮助团队理解系统的数据结构。实体关系图,作用就是使用图形描述现实业务的实体及其属性,以及不同实体之间的关系。
2.1、作用:
-
数据库设计的蓝图:ER图以图形化方式展示实体、属性及其关系,是构建数据库表结构的基础,有助于定义表、字段和外键关系,从而确保数据库结构合理高效。
-
明确数据需求与结构:通过实体和属性的定义,ER图帮助分析系统需要存储哪些数据,并展示数据之间的关联方式(如1:1、1:N、M:N)。
-
系统分析与需求建模:分析师使用ER图理解现有系统或新系统的数据需求,识别关键数据实体及其关系,为业务流程建模提供支持。
-
减少数据冗余、提升数据质量:ER图通过规范化设计和关系梳理,帮助减少冗余数据,提高数据库一致性与查询效率。
-
促进团队沟通协作:ER图作为可视化模型,使技术人员与非技术人员都能理解数据结构,促进沟通并确保对系统数据模型的统一认知。
-
辅助数据库调试与维护:将现有数据库与ER图对照,可快速发现设计缺陷或潜在错误,简化调试与后续维护工作。
2.2、核心要素
-
实体: 客观存在并可相互区分的事物,类似于学生表、班级表、课程表......在ER图中使用矩形框表示
-
属性: 用无向边连接到其所属的实体或关系,学生ID、姓名name......在ER图中用圆形框表示
-
关系: 用菱形表示,并用无向边连接到相关的实体,边上注明联系的类型(1:1,1:N, M:N)
实体与实体之间 的关系:
一对一的关系:一个身份证对应一个学生
一对多的关系:一个班级可以有多个学生
多对多的关系:一个学生可以选学多门课程,一个课程可以被多个学生选择
对于多对多的时候,可以建立一个关于两个关系的表作为桥梁,包括表1 的和表2的primary key 的主键,和自己的编号,叫做关联表,不出现在ER图里面,存在与表中,例如:

三、数据库的三大范式
3.1、三大范式:
大佬们根据过往的经验总结出来的设计标准

1NF第一范式:表中的每一个字段都是不同分割的原子字段(强调字段原子性)
不遵循第一范式出现的问题---->字段非原子:机会导致表中出现大量的数据冗余,且查询的时候也极为麻烦

2NF第二范式:前置要求:满足第一范式,主要针对组合主键,描述表中所有字段应该对主键完全依赖。 一旦我们的字段把并没有完全依赖我们的主键,而是只依赖了一部分,那必然会造成数据冗余。

**3NF第三范式:**必须满足第二范式,表中所有字段都应该与主键直接依赖,不能间接依赖。
危害:一旦表中出现了间接依赖,必然表中会出现大量数据冗余

第四范式(4NF):在3NF的基础上,消除 多值依赖。 即如果一个表中的某些列可以取多个值(比如多个电话号码),那么这些多值依赖的列需要剥离出来,创建新表进行处理,确保数据的唯一性和正确性。
第五范式 (5NF):在4NF的基础上,消除奇异化(即超规范化)。 奇异化指的是为了消除多值依赖而剥离出来的列,如果发现这些列之间存在 依赖关系 (称为" 连接依赖 "),则需要再次拆分为独立的表进行处理。
3.2、数据库范式
数据库范式是用于减少冗余、避免异常并提升一致性的数据库设计规则,常见包括 1NF、2NF、3NF、BCNF、4NF、5NF,其中 1NF 要求字段原子性,2NF 要求完全依赖主键,3NF 消除传递依赖,BCNF 要求所有决定因素为候选键,4NF 处理多值依赖,5NF 处理连接依赖。
数据库表设计中可能存在的问题:
-
数据冗余:相同数据在多个地方重复存储,浪费空间,且更新时要修改多处,容易出错
-
更新异常:修改一行数据时,可能导致数据不一致。
-
插入异常:想插入某些信息,却因为缺少其他信息而无法插入。
-
删除异常:删除一行数据时,可能意外丢失其他有用信息。
四、多表连接
表连接,会造成大量额数据膨胀,降低效率,因此:
-
阿里约束,aql语句中,不允许出现3张及以上的表连接
-
连接的时候,设计了多种不同的连接方式(内连接、外连接(左/右连接)子连接)
-
并且连接的时候,还会设置连接条件,避免全量连接。
插入数据:
sql
DROP TABLE IF EXISTS students;
DROP TABLE IF EXISTS classes;
CREATE TABLE classes (
class_id INT PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(32),
teacher VARCHAR(32)
);
CREATE TABLE students (
student_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32),
class_id INT
);
INSERT INTO classes (class_id, class_name, teacher) VALUES
(101, '一班', '王老师'),
(102, '二班', '李老师'),
(105, '五班', '张老师');
INSERT INTO students (student_id, name, class_id) VALUES
(1, '张三', 101),
(2, '李四', 101),
(3, '王五', 102),
(4, '赵六', 103);

4.1、内连接INNER JOIN
两张表连接,取交集的属性
sql
SELECT s.student_id, s.name, c.class_name, c.teacher
FROM students s
INNER JOIN classes c ON s.class_id = c.class_id;

4.2、外连接(左连接和右连接)
左连接:以左表为基础,从右表中查找符合条件的数据进行连接,如果没有符合条件的数据;left join
sql
SELECT s.student_id, s.name, c.class_name, c.teacher
FROM students s
LEFT JOIN classes c ON s.class_id = c.class_id;

右连接:以右表为基础,从左表中查找符合条件的数据进行连接 right join
sql
SELECT s.student_id, s.name, c.class_name, c.teacher
FROM students s
RIGHT JOIN classes c ON s.class_id = c.class_id;
4.3、笛卡尔积
sql
SELECT * FROM students JOIN classes;
for a in 左表: # 外层循环,m 次
for b in 右表: # 内层循环,n 次
输出 (a 的所有列 + b 的所有列)
外层 m 次 × 内层 n 次 = m × n 行 。列数则是两表列数相加。
