数据库表的设计

一、表的约束

表的约束是数据库中用于确保数据完整性和一致性的规则,主要包括主键约束、外键约束、唯一约束等。

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 行 。列数则是两表列数相加。

相关推荐
Pniubi1 小时前
怎么排查慢SQL,怎么解决?
java·数据库·sql
Decay__5 小时前
VMware 安装 SQL Server 双节点 AlwaysOn 可用性组教程 —— WSFC、AG 与侦听器完整部署(系列核心篇)
运维·数据库·sql·mysql·架构
茶栀(*´I`*)6 小时前
SQL 性能分析:从慢查询日志到执行计划的完整定位方法
数据库·sql
艺杯羹6 小时前
1Panel 装好后 ufw 仍是 inactive:一次服务器安全盲区复盘
服务器·数据库·安全·网络安全·系统架构·实战·1panel
code2cat7 小时前
【随笔】MCP工具重试:任务已经创建,响应丢了怎么办
数据库·mcp
wuyk5559 小时前
Spider-第10讲-SQLite持久化与去重
数据库·爬虫·sqlite
乌暮10 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
小此方11 小时前
Re:Mysql数据库基础篇(十二):什么是事务、事务解决了什么问题、事务的四大属性、事务的操作方式
数据库·mysql
flash俊杰12 小时前
v0.5-01 让桌面 AI 助手记得住:预算组装、递归摘要、长期记忆与评估基线
数据库·安全·架构