数据库概论速成期中版
文章目录
- 引论
- 数据库用户
- Casual users
- Naive users
- Application programmers
- Database administrators
- 关系模型
- CAP数据库
- 两种描述关系数据库的方式
- 简单总结
- 第一范式规则
- 第二范式规则
- 举个例子
- 符合第二规则的操作
- 不符合第二规则的操作
- 第三范式规则
- key,superkey,null values,主键,候选键
- 1. 什么是Keys(键)?
- 2. 什么是Superkeys(超键)?
- 3. 什么是Null Values(空值)?
- 4. 什么是主键(Primary Key)?
- 5. 什么是候选键?
- 集合运算
- 1. **并(Union)**
- 2. **交(Intersection)**
- 3. **差(Difference)**
- 4. **笛卡尔积(Cartesian Product)**
- 5. **投影(Projection)**
- 6. **选择(Selection)**
- 7. **自然连接(Natural Join)**
- 8. **除(Division)**
- sql语言查询
- DDL
- 1. **主要功能**
- 2. **常见 DDL 语句**
- (1) **CREATE** - 创建数据库对象
- (2) **ALTER** - 修改数据库对象
- (3) **DROP** - 删除数据库对象
- (4) **TRUNCATE** - 清空表数据
- 3. **特点**
- 数据类型
- 实体完整性规则和参照完整性规则
- 1. **实体完整性规则**(Entity Integrity Rule)
- 2. **参照完整性规则**(Referential Integrity Rule)
- 相关语句
- count
- sum
- avg
- max,min
- group by
- 需求:只看人数大于 1 的班级
- order by
- into
- in
- 相关子查询
- 子查询比较关键词
- join
- **INNER JOIN**(默认 JOIN)
- **LEFT JOIN**(LEFT OUTER JOIN)
- **RIGHT JOIN**(RIGHT OUTER JOIN)
- **FULL JOIN**(FULL OUTER JOIN)
- **CROSS JOIN**(交叉连接)
- exists
- union
- INTERSECT 运算符
- EXCEPT运算符
- 联接查询
- 等值联接
- 自然联接
- 非等值连接
- 自身连接
- 外联接
- 复合条件联接
- update
- delete
- 索引
- 唯一索引 (Unique Index)
- 聚集索引(CLUSTERED INDEX)
- 非簇索引 (Nonclustered Index)
- 存储程序和触发器
- 存储过程
- 啥是 Stored Procedure(存储过程)?
- 触发器
- 啥是触发器?
- 触发器的类型
- 啥是 inserted 和 deleted 表?
- 它们在啥时候有数据?
- 数据库设计
- 数据库设计是啥?
- 概念模型设计(基于 E-R 图)
- 范式设计(基于规范化理论)
- 啥是依赖?
- ER相关概念
- 单值 (Single-Valued) 和 多值 (Multi-Valued)
- 对方实体 (Other Entity)
- max-card(E, R)(最大基数)
- min-card(E, R)(最小基数)
- 二元关系转换
- 弱实体
- 啥是弱实体?
- 啥是泛化层次结构?
- 约束条件
- 函数依赖
- 先复习:啥是函数依赖?
- Armstrong 公理(三大基本规则)
- (1) 自反律 (Reflexivity Rule)
- (2) 增补律 (Augmentation Rule)
- (3) 传递律 (Transitivity Rule)
- (1) 并规则 (Union Rule)
- (2) 分解规则 (Decomposition Rule)
- (3) 伪传递律 (Pseudo-Transitivity Rule)
- 闭包 (Closure) 的概念
- 覆盖和最小覆盖
- 1. 啥是覆盖 (Cover)?
- 2. 啥是最小覆盖 (Minimal Cover)?
- 求最小覆盖的步骤
- 步骤 1:分解右边(单属性化)
- 步骤 2:去掉冗余属性(简化左边)
- 步骤 3:去掉冗余依赖
- 最终最小覆盖:
- 再来个复杂点的例子
- 关系模式的分解
- 关系模式分解的两个关键性质
- 1. 无损连接 (Lossless Join)
- 2. 依赖保持 (Dependency Preservation)
- 完整性约束,视图,安全,系统目录
- 完整性约束 (Integrity Constraints)
- 视图 (Views)
- 安全 (Security)
- 数据库安全的四个等级
- 1. 系统级安全 (System-Level Security)
- 2. 数据库级安全 (Database-Level Security)
- 3. 对象级安全 (Object-Level Security)
- 数据级安全 (Data-Level Security)
- 系统目录 (System Catalogs)
- 练习题
- 文件系统和DBMS的四个主要区别
- 解释物理独立性,以及它在数据库系统中的重要性。
- 找出候选键
- 根据笔者的学习过程的记录,如果你的学校所学内容与这些不符合,没必要花大精力在上面。
引论
数据库用户
Casual users
临时用户,对sql语言有一定了解并能查询
Naive users
本地用户,通过菜单应用程序执行sql,无需sql语句
Application programmers
应用程序员,负责编写本地用户使用的程序
Database administrators
数据库管理员,负责设计和维护数据库的专业人员
工作包括:
- 创建数据库
- 创建表格
- 执行幕后任务
- 页面的物理布局
关系模型
CAP数据库
两种描述关系数据库的方式
在关系型数据库里,描述数据的方式有两套术语,虽然说的是一回事,但名字听起来有点不同:
- 第一套术语:表(Tables)、列(Columns)、行(Rows)
- 表(Tables):就像一张Excel表格,里面装了一堆数据。比如“学生表”存了所有学生的信息。
- 列(Columns):表里的每一列,就像Excel的标题,比如“姓名”“学号”“年龄”,每列存一种信息。
- 行(Rows):表里的每一行,就是一条具体的数据记录。比如“张伟, 001, 18”是一行,代表一个学生的信息。
- 第二套术语:关系(Relations)、元组(Tuples)、属性(Attributes)
- 关系(Relations):其实就是“表”的学名。因为表里的数据是通过“关系”组织起来的,所以叫关系。跟“表”是一个意思。
- 元组(Tuples):就是“行”的学名。每行数据是一个完整的记录,数学上叫“元组”。比如“张伟, 001, 18”就是一个元组。
- 属性(Attributes):就是“列”的学名。每列表示数据的一个特性,数学上叫“属性”。比如“姓名”“学号”就是属性。
简单总结
- 表 = 关系:都是指整个表格。
- 行 = 元组:都是指表格里的一条记录。
- 列 = 属性:都是指表格里的一个字段(标题)。
第一范式规则
第一范式是关系型数据库的一个基本规则,它要求表的每一列(字段)里的数据必须是单一的、不可再分的原子值。换句话说:
- 不能在一列里塞多个值(比如一个格子里放一堆东西)。
- 不能让一列的值有复杂的内部结构(比如嵌套一个表格或记录)。
只有满足这个要求的表,才算是达到了第一范式。
要满足1NF,如果一个格子内的属性是同一标签,得把多值拆开,让每列只存单一值。
假如有个表这样:
学号 | 姓名 | 联系方式 |
---|---|---|
001 | 张伟 | 电话:123456, 邮箱:zw@xx.com |
002 | 李明 | 电话:789012, 邮箱:lm@yy.com |
联系方式列有内部结构(电话和邮箱混在一起),不符合1NF。改成1NF可以拆成两列:
学号 | 姓名 | 电话 | 邮箱 |
---|---|---|---|
001 | 张伟 | 123456 | zw@xx.com |
002 | 李明 | 789012 | lm@yy.com |
第二范式规则
这个规则说,在关系型数据库里,你想找表里的某一行数据(记录),只能通过行里的内容(也就是每一列的具体值)来查找。换句话说,你得告诉数据库“我要找哪些值”,而不是靠其他方式(比如行的位置或顺序)来挑数据。
举个例子
假设你有一个学生表:
学号 | 姓名 | 年龄 |
---|---|---|
001 | 张伟 | 18 |
002 | 李明 | 19 |
003 | 王芳 | 18 |
符合第二规则的操作
如果你想找某个学生,可以用列里的值作为条件,比如:
- “找出姓名是‘张伟’的行” → 数据库返回:001, 张伟, 18。
- “找出学号是‘002’的行” → 数据库返回:002, 李明, 19。
- “找出年龄是18的行” → 数据库返回:001, 张伟, 18 和 003, 王芳, 18。
这些操作都是通过内容(学号、姓名、年龄的具体值)来找数据,符合第二规则。
不符合第二规则的操作
如果你说:
- “给我表里第2行的数据” → 数据库会说“啥?不行!” 因为它不认行的位置。
- “给我按插入顺序的第1条数据” → 也不行,因为数据库不关心数据插入的顺序。
为什么不行?因为数据库里的行可能会因为排序、分布式存储或其他原因,位置随时变。如果靠位置找数据,结果可能不靠谱。
第三范式规则
第三规则说,在关系型数据库的同一个表里,不能有两行数据完全一模一样。也就是说,表里的每一行(元组)在所有列的值上都得是独一无二的,不能有“双胞胎”行。
在实际中,这通常靠主键(Primary Key)来保证。主键是一列(或几列),它的值在每行都不同,用来区分每一行。
key,superkey,null values,主键,候选键
1. 什么是Keys(键)?
键是数据库表中用来唯一标识一行数据或建立表之间关系的列(或几列)。它就像一个“标签”,让数据库能快速找到或区分特定的行。
- 作用
- 确保每行数据独一无二(区分行)。
- 帮助表之间建立联系(比如通过外键)。
- 例子:在一个学生表里,“学号”可以是键,因为每个学生的学号都不一样,能用来找特定学生。
2. 什么是Superkeys(超键)?
超键是能唯一标识表中每一行的列的集合,可以包含一列或多列。简单说,超键是“够用”的键,可能包含了比实际需要更多的列。
-
特点
- 超键只要能保证每行唯一就行,哪怕包含了不必要的列。
- 超键可能有很多个。
-
例子
假设学生表有这些列:
学号,姓名, 年龄
- {学号} 是一个超键,因为学号本身就能唯一标识每行。
- {学号, 姓名} 也是超键,因为学号加姓名肯定也能唯一标识(虽然姓名其实没必要)。
- {学号, 姓名, 年龄} 还是超键,包含了所有列,肯定唯一。
但超键可能“太胖”,包含多余的列,所以我们会从中挑出更精简的键(比如候选键或主键)。
3. 什么是Null Values(空值)?
空值(Null)表示数据缺失或未知,不是0,也不是空字符串,而是一个特殊的标记,表示“这里没值”。
4. 什么是主键(Primary Key)?
主键是表中唯一标识每一行的列(或几列),是从超键中挑出来的一个最精简、最合适的键。每个表只能有一个主键。
- 特点:
- 唯一性:主键的值在每行都不同,不能重复。
- 非空:主键列不能有空值(Null)。
- 唯一选择:一个表只能定一个主键。
- 作用:
- 区分表中的每一行(像身份证号)。
- 作为其他表的外键,建立表之间的关系。
5. 什么是候选键?
候选键是关系型数据库中能唯一标识表中每一行的列(或列的组合),而且是最精简的。换句话说,它是“够用且不浪费”的键,能保证每行不重复,但没有多余的列。
- 特点
- 唯一性:候选键的值在每行都不同,不能重复。
- 非空:候选键的列不能有空值(Null)。
- 最精简:不能去掉任何一列,否则就没法唯一标识行了。
- 一个表可以有多个候选键,但最终只会选一个作为主键。
- 和超键的区别
- 超键(Superkey)可能包含多余的列,比如“学号+姓名”也能唯一标识,但“姓名”其实没必要。
- 候选键是超键的“瘦身版”,只保留最必要的列。
- 和主键的区别
- 候选键是“候选人”,表里可能有好几个。
- 主键是从候选键中挑一个“上岗”的,只有一个。
集合运算
-
表 R (学生信息)
学号 (ID) 姓名 (Name) 1 张三 2 李四 3 王五 -
表 S (选课信息)
学号 (ID) 姓名 (Name) 2 李四 4 赵六
1. 并(Union)
-
定义:合并 R 和 S的所有元组,去除重复。
-
操作: R ∪ S R∪S R∪S
-
结果
学号 (ID) 姓名 (Name) 1 张三 2 李四 3 王五 4 赵六
SELECT * FROM R
UNION
SELECT * FROM S;
2. 交(Intersection)
-
定义:返回 R 和 S 共有的元组。
-
操作:R ∩ S
-
结果
学号 (ID) 姓名 (Name) 2 李四
SELECT R.* FROM R
INNER JOIN S ON R.ID = S.ID AND R.Name = S.Name;
3. 差(Difference)
-
定义:返回在 R 中但不在 S 中的元组。
-
操作:R−S
-
结果
学号 (ID) 姓名 (Name) 1 张三 3 王五
4. 笛卡尔积(Cartesian Product)
-
定义:将 R 和 S 的每对元组组合。
-
操作:R×S
-
结果
R.ID R.Name S.ID S.Name 1 张三 2 李四 1 张三 4 赵六 2 李四 2 李四 … … … …
SELECT * FROM R CROSS JOIN S;
5. 投影(Projection)
-
定义:从 R 中选择“姓名”列,去除重复。
-
操作: π N a m e ( R ) πName(R) πName(R)
-
结果
姓名 (Name) 张三 李四 王五 -
SQL
SELECT DISTINCT Name FROM R;
-
说明:仅返回唯一的姓名。
6. 选择(Selection)
-
定义:从 R 中选择学号大于 1 的元组。
-
操作: σ I D > 1 ( R ) σID>1(R) σID>1(R)
-
结果
学号 (ID) 姓名 (Name) 2 李四 3 王五
7. 自然连接(Natural Join)
-
定义:按同名属性(ID 和 Name)连接 R 和 S 。
-
操作: R ⋈ S R⋈S R⋈S
-
结果
学号 (ID) 姓名 (Name) 2 李四 -
SQL
SELECT * FROM R NATURAL JOIN S;
-
说明:只保留 ID 和 Name 均相等的元组。
8. 除(Division)
-
场景:假设新表T
记录学生选课:
-
表 T (选课记录)
学号 (ID) 课程 (Course) 1 数学 1 英语 2 数学 -
表 U (必修课)
课程 (Course) 数学 英语
-
-
定义:找出选修了 U 中所有课程的学生。
-
操作: T ÷ U T÷U T÷U
-
结果
学号 (ID) 1 SELECT ID FROM T GROUP BY ID HAVING COUNT(DISTINCT Course) = (SELECT COUNT(*) FROM U) AND NOT EXISTS (SELECT Course FROM UWHERE Course NOT IN (SELECT Course FROM T WHERE T.ID = T.ID) );
sql语言查询
DDL
SQL 的 DDL(Data Definition Language,数据定义语言) 用于定义和管理数据库结构,包括创建、修改、删除数据库对象(如表、视图、索引等)。DDL 语句主要操作数据库的模式(Schema),不涉及数据内容操作。以下是 DDL 的简单介绍及常见语句:
1. 主要功能
- 创建数据库对象(如表、数据库、索引)。
- 修改现有对象的结构(如添加列、修改数据类型)。
- 删除对象(如删除表、视图)。
- 定义约束(如主键、外键、唯一约束)。
2. 常见 DDL 语句
(1) CREATE - 创建数据库对象
-
用途:创建数据库、表、视图、索引等。
-
示例
-- 创建数据库 CREATE DATABASE school; -- 创建表 CREATE TABLE students ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT );
-
说明:定义表结构,指定列名、数据类型和约束(如 PRIMARY KEY、NOT NULL)。
(2) ALTER - 修改数据库对象
-
用途:修改表结构,如添加列、删除列、更改数据类型等。
-
示例
-- 添加列 ALTER TABLE students ADD email VARCHAR(100); -- 修改列数据类型 ALTER TABLE students MODIFY age SMALLINT; -- 删除列 ALTER TABLE students DROP COLUMN email;
-
说明:用于调整已有对象的定义。
-
在sql-server中添加列不需要添加列名,但删除需要
(3) DROP - 删除数据库对象
-
用途:删除表、数据库、索引等。
-
示例
-- 删除表 DROP TABLE students; -- 删除数据库 DROP DATABASE school;
-
说明:删除操作不可恢复,需谨慎使用。
(4) TRUNCATE - 清空表数据
-
用途:删除表中所有数据,但保留表结构。
-
示例
TRUNCATE TABLE students;
-
说明:与 DELETE 不同,TRUNCATE 不记录日志,执行更快,但无法回滚。
3. 特点
- 自动提交:DDL 语句执行后自动提交(COMMIT),无法回滚(ROLLBACK)。
- 结构操作:只操作数据库或表的结构,不涉及数据内容(数据操作由 DML 负责)。
- 权限要求:通常需要较高的权限(如管理员权限)来执行 DDL。
数据类型
数据类型 | 描述 | 示例值 | 适用场景 |
---|---|---|---|
INT | 整数(通常 4 字节,范围约 -2^31 到 2^31-1) | 123, -456 | 存储整数,如 ID、年龄 |
BIGINT | 大整数(通常 8 字节,范围更大) | 123456789012 | 存储大范围整数,如计数器 |
FLOAT | 单精度浮点数(近似值) | 3.14, -0.001 | 存储小数,精度要求不高 |
DOUBLE | 双精度浮点数(更高精度) | 3.1415926535 | 存储高精度小数,如科学计算 |
DECIMAL(m,d) | 定点小数(m 位总长度,d 位小数) | 123.45 (DECIMAL(5,2)) | 存储精确小数,如货币金额 |
CHAR(n) | 固定长度字符串,最大 n 个字符 | "ABC " (CHAR(5)) | 固定长度文本,如状态码、邮编 |
VARCHAR(n) | 可变长度字符串,最大 n 个字符 | “ABC” (VARCHAR(5)) | 可变长度文本,如姓名、地址 |
TEXT | 大文本数据,无固定长度限制(视数据库而定) | “This is a long text…” | 存储大段文本,如文章、描述 |
DATE | 日期(年-月-日) | 2023-04-27 | 存储日期,如生日、注册日期 |
DATETIME | 日期和时间 | 2023-04-27 14:30:00 | 存储时间戳,如创建时间 |
BOOLEAN | 布尔值(真/假) | TRUE, FALSE | 存储开关状态,如启用/禁用 |
BLOB | 二进制大对象,用于存储二进制数据 | 图像、文件数据 | 存储图片、视频等二进制文件 |
varchar和char的区别在于,varchar占用的存储空间是根据当前字符长度动态申请的,加上一个数据长度标识符,优点是节约空间,缺点是查找时需要通过标识符进行一定的计算,会比char略慢。
实体完整性规则和参照完整性规则
1. 实体完整性规则(Entity Integrity Rule)
啥意思?
- 实体完整性是保证数据库里每条记录(行)都有一个唯一标识,而且这个标识不能是空的。简单说,就是每行数据得有个“身份证号”,而且不能缺。
咋做到?
-
主要靠
主键(Primary Key)
- 每张表的主键列(比如 id)必须唯一,不能有两行数据的主键值一样。
- 主键列不能是 NULL(空值),因为 NULL 代表“不知道”,没法当标识。
2. 参照完整性规则(Referential Integrity Rule)
啥意思?
- 参照完整性是保证表之间的关系靠谱。简单说,如果一张表里的某个值(外键)引用了另一张表的数据,那引用的值必须在另一张表里真实存在,或者是空的。
咋做到?
- 靠**外键(Foreign Key)**约束:
- 外键列的值要么是主表中主键的合法值,要么是 NULL。
- 如果主表的数据被删了或改了,子表的外键得跟着调整(比如删除、置空,或禁止操作)。
为啥重要?
- 防止“孤儿数据”。比如,订单表里的 customer_id 必须对应客户表里存在的客户,不然订单就不知道是谁的了。
3.用户定义完整性
录入的数据必须在用户定义的范围内
相关语句
插入多条数据
insert into test values('2','enoch',19),('3','llk',20);
去重选择
select distinct name from test;
选择前几条记录,order默认从小到大排序
select top 4 * from test order by age;
count
用 test 表来试试:
SELECT COUNT(*) AS 总行数, COUNT(phone) AS phone列非空个数, COUNT(DISTINCT phone) AS phone列不重复个数
FROM test;
结果:
总行数 | phone列非空个数 | phone列不重复个数
2 | 2 | 2
sum
SELECT SUM(distinct CAST(id AS INT)) AS id总和
FROM test;
cast函数将字符转为数字,去重后求和
avg
求平均值
SELECT AVG(id) AS id平均值,AVG(DISTINCT id) AS id不重复平均值
FROM test;
max,min
同理
group by
需求:只看人数大于 1 的班级
SELECT
class_id, COUNT(*) AS 人数
FROM test
GROUP BY class_id
HAVING COUNT(*) > 1;
结果:
class_id | 人数
---------|------
1 | 2
2 | 2
咋算的?
- 先按 class_id 分组:1 班 2 人,2 班 2 人,3 班 1 人。
- HAVING COUNT(*) > 1 筛选,只留下人数大于 1 的组,所以 3 班被过滤掉了。
group by,相当于把之前的表按照一定的限制条件分成更小的几个表,count函数在这些小表中分别执行
order by
select * from test order by grade desc
改为从大到小排序(默认是升序
into
select * from test into new_table
将查询数据输出到新表中
in
... in (select ...)
in找集合里的东西,()中是子查询
作用域问题,主查询和子查询看不到对方执行查询的列的细节,只能看到执行后返回的结果
相关子查询
子查询需要依赖主查询中的一些数据
SELECT name, class_id,(SELECT COUNT(*) FROM test t2 WHERE t2.class_id = t1.class_id) AS 班级人数
FROM test t1;
通过建立别名,每次都拿到外围的t1.class_id作为限制条件
子查询比较关键词
列名 比较符 (ANY|ALL) (子查询)
- 列名:你要比较的值(比如 id)。
- 比较符:像 >、<、>= 啥的。
- 子查询:返回一堆值(比如一列 id)。
- ANY/SOME:ANY 和 SOME 是一个意思(SQL Server 里一样),表示“跟子查询里的任意一个值比”。
- ALL:表示“跟子查询里的所有值比”。
eg.
SELECT name
FROM test
WHERE id > ANY (SELECT min_id FROM classes);
join
SELECT t.name
FROM test t
JOIN classes c ON t.id > c.min_id;
这句 SQL 是在查 test 表和 classes 表,挑出 test 表里的 name,但有个条件:test 表里的 id 要大于 classes 表里的 min_id。
简单说,就是找“学生 ID 比某些班级的 min_id 大的学生名字”。每一行对应匹配
-
JOIN 是 SQL 里的“连表神器”,专门把两张表(或更多)按条件拼起来,生成新的结果。
-
就像你有两个名单,一个是学生名单(test),一个是班级名单(classes),你想把学生的班级信息拼到一起,JOIN 就能干这活儿。
SELECT t.name, c.class_name
FROM test t
INNER JOIN classes c ON t.class_id = c.class_id;
保留左表(test)的所有行,右表(classes)匹配不上的就填 NULL。
就像“所有学生都列出来,找不到班级的就写空”。
name | class_name
------|------------
enoch | Class A
lally | Class A
alice | Class B
bob | Class B
cathy | NULL
保留右表(classes)的所有行,左表(test)匹配不上的填 NULL。
就像“所有班级都列出来,找不到学生的就写空”。
name | class_name |
---|---|
enoch | Class A |
lally | Class A |
alice | Class B |
bob | Class B |
cathy | Class C |
NULL | Class D |
两边表的所有行都保留,匹配不上的填 NULL。
就像“学生和班级都列出来,找不到对应信息的就写空”。
不写 ON 条件,把两张表每行都组合一次,生成“笛卡尔积”。test 有 5 行,classes 有 4 行,生成 5 × 4 = 20 行:
exists
简单说:
- EXISTS 是个“有没有”的检查工具,用来判断子查询有没有返回结果。
- 如果子查询有哪怕一行结果,EXISTS 就返回 TRUE(真);如果子查询没结果,就返回 FALSE(假)。
- 通常跟子查询一起用,写成 WHERE EXISTS (子查询)。
union
union语句联合查询,要求列数必须相同,默认去重,如果要保留重复数据,可以使用union all select
INTERSECT 运算符
啥是 INTERSECT?
- INTERSECT 是个“找交集”的工具,把两个查询的结果比一比,只留下两边都有的行。
- 就像你有两份名单,一个是篮球队的,一个是足球队的,INTERSECT 能挑出既打篮球又踢足球的人。
咋用?
-
语法:
查询1 INTERSECT 查询2;
-
关键点
- 两个查询的列数得一样,类型得兼容(跟 UNION 一样)。
- 自动去重(只留一份重复的)。
EXCEPT运算符
啥是 EXCEPT?
- EXCEPT 是个“找差集”的工具,把第一个查询的结果拿出来,减去第二个查询的结果,只留下第一个查询独有的行。
- 就像你有两份名单,一个是全校学生,一个是篮球队的,EXCEPT 能挑出不在篮球队的学生。
联接查询
等值联接
啥意思?
- 等值联接就是用“等于”(=)来匹配两张表的列,找两边值相等的行。
- 就像找“学生和班级匹配的记录”,用 class_id 相等来连。
SELECT t.name, c.class_name
FROM test t
JOIN classes c ON t.class_id = c.class_id;
自然联接
啥意思?
- 自然联接(NATURAL JOIN)是一种特殊的等值联接,自动用两张表里同名列来做等值匹配。
- 不用写 ON,SQL 自己找同名列(像 class_id)来连,还会把重复的列去掉(结果里只留一份 class_id)。
- 相当于是自动的等值连接
SELECT t.name, c.class_name
FROM test t
NATURAL JOIN classes c;
非等值连接
就是把=号换成><这样的比较符号
SELECT t.name, c.class_name
FROM test t
JOIN classes c ON t.id > c.min_id;
自身连接
啥意思?
- 自身联接是自己跟自己连(同一张表),用别名区分两份“自己”。
- 就像找“同一个班级的学生对”(学生跟学生比)。
SELECT t1.name AS 学生1, t2.name AS 学生2, t1.class_id
FROM test t1
JOIN test t2 ON t1.class_id = t2.class_id
WHERE t1.id < t2.id;
t1 和 t2 是 test 表的两个副本。
t1.class_id = t2.class_id:找同班的。
t1.id < t2.id:避免重复对(比如 enoch-lally 和 lally-enoch 只留一个)。
为啥用?
- 找表内关系,比如“同班学生”、“员工和他的上级”。
外联接
啥意思?
- 外联接(OUTER JOIN)保留至少一张表的所有行,匹配不上的填 NULL。
- 有三种:
- LEFT OUTER JOIN:左表全留,右表没匹配填 NULL。
- RIGHT OUTER JOIN:右表全留,左表没匹配填 NULL。
- FULL OUTER JOIN:两表都留,没匹配填 NULL。
复合条件联接
啥意思?
-
复合条件联接是用多个条件(用 AND、OR 组合)来匹配两张表。
-
就像“班级匹配且 ID 大于某个值”。
-
SELECT t.name, c.class_name FROM test t JOIN classes c ON t.class_id = c.class_id AND t.id > 2;
update
update test
set age=3
where name='lally';
delete
delete from test
where name='lally';
索引
唯一索引 (Unique Index)
啥意思?
- 唯一索引保证索引列(或多列组合)的值不能重复。
- 就像给学生编号(id),每个人的编号必须唯一,不能有重复。
create unique index my_index on test(name);
聚集索引(CLUSTERED INDEX)
啥意思?
- 簇索引(聚集索引)决定表数据的物理存储顺序,有点像按字母顺序排书的书架。
- 一张表只能有一个簇索引(因为物理顺序只能有一种)。
CREATE CLUSTERED INDEX idx_clustered_class_id ON test(class_id);
通常自动创建:如果你给表加了 PRIMARY KEY,SQL Server 默认会建一个簇索引(除非你指定主键用非簇索引)。
特点:
- 决定数据物理顺序,查询范围(如 WHERE class_id BETWEEN 1 AND 2)超快。
- 一张表只能有一个簇索引。
- 插入数据时可能慢(因为要保持顺序,可能会移动数据)。
为啥用?
- 适合经常按某列排序或范围查询的场景(像 class_id 这种)。
- 主键默认用簇索引(比如 id)。
非簇索引 (Nonclustered Index)
啥意思?
- 非簇索引(非聚集索引)是个“独立目录”,不改变数据物理顺序,只存一份“指引”。
- 就像书后面加了个索引页,告诉你“名字”在哪页,但书本身还是按章节排。非聚集索引将数据在内存中的索引位置记录下来,查找的时候就会很快。
CREATE NONCLUSTERED INDEX idx_nonclustered_name ON test(name);
为啥用?
- 适合经常查但不排序的列(像 name、phone)。
- 配合 WHERE、JOIN 加速查询。
存储程序和触发器
存储过程
啥是 Stored Procedure(存储过程)?
简单说:
- 存储过程是一堆预先写好的 SQL 语句,存成一个“程序”,放在数据库里,随时可以调用。
- 就像你把做蛋糕的步骤(加面粉、打鸡蛋、烤)写成一个食谱,存起来,啥时候想吃蛋糕就直接拿出来用,不用每次都重新写步骤。
CREATE PROCEDURE 存储过程名字
AS
BEGIN-- 你的 SQL 语句
END;
然后用exec或execute调用
用@传入参数
输入输出参数的区别
CREATE PROCEDURE 存储过程名字@参数1 数据类型, -- 输入参数@参数2 数据类型 OUTPUT -- 输出参数
AS
BEGIN-- 逻辑
END;
结合返回值和if语句使用存储过程
create procedure find3@findid varchar(10)
as
begindeclare @status int;if not exists (select * from test where id=@findid)beginset @status=0;return @status;end;set @status=1;return @status;
end;declare @sta int;
exec @sta=find3 @findid=1;
if @sta=1print '查到了';
else if @sta=0print '查不到';
调用输出需要需要先声明一个变量存储
DECLARE @Count INT; -- 声明变量接输出
EXEC CountStudentsByClass @ClassID = 1, @StudentCount = @Count OUTPUT;
PRINT 'Number of students: ' + CAST(@Count AS VARCHAR(10));
修改存储过程可以使用alter
触发器
啥是触发器?
简单说:
- 触发器是一段特殊的存储过程,绑定在表上,当表发生特定操作(比如插入、更新、删除)时自动“触发”运行。
- 就像你家装了个门铃,有人开门(操作表)时,门铃自动响(触发器跑起来),还能顺手干点事(比如记录谁开了门)。
- 绑定到特定的表上
CREATE TRIGGER 触发器名字
ON 表名
FOR/AFTER/INSTEAD OF [INSERT, UPDATE, DELETE]
AS
BEGIN-- 你的 SQL 逻辑
END;
触发器的类型
SQL Server 里有三种触发器:
-
AFTER 触发器
(也叫 FOR 触发器):
- 在操作(INSERT、UPDATE、DELETE)完成后再跑。
- 最常用,比如记录操作日志。
-
INSTEAD OF 触发器
- 代替操作执行,操作本身不执行,触发器里的逻辑跑。
- 比如想阻止删除,改成“假装删”。
-
DDL 触发器
(不针对表,针对数据库操作):
- 响应数据库级操作(像 CREATE TABLE、DROP TABLE),这里不细讲。
create trigger my_tri
on test
after insert,update
as
beginupdate testset remark='kid'where age<3;
end;insert test values(4,'jjj',1,null)
select * from test;
啥是 inserted 和 deleted 表?
简单说:
- inserted 和 deleted 是 SQL Server 在触发器里提供的两个临时虚拟表,用来记录表数据的变化。
- 它们只在触发器运行时存在,触发器跑完就没了。
作用:
- inserted:存“新数据”(插入或更新后的数据)。
- deleted:存“旧数据”(删除或更新前的数据)。
- 就像你改作业,deleted 是改之前的草稿,inserted 是改完的新稿。
关键点:
- 这俩表的结构跟原表(触发器绑定的表)一样。
- 只在触发器里能用,存储过程或其他地方用不了。
它们在啥时候有数据?
inserted 和 deleted 表的内容取决于触发器的操作类型(INSERT、UPDATE、DELETE):
操作 | inserted 表 | deleted 表 |
---|---|---|
INSERT | 新插入的行 | 空 |
UPDATE | 更新后的行 | 更新前的行 |
DELETE | 空 | 被删除的行 |
数据库设计
数据库设计是啥?
简单说:
- 数据库设计就是规划怎么存数据,设计表结构,确保数据好存、好查、不乱。
- 就像建房子,先画图纸(概念模型),再按规则砌墙(范式设计),让房子结实又好用。
目标:
- 数据不重复(节省空间)。
- 查询快(性能好)。
- 好维护(改数据不出错)。
概念模型设计(基于 E-R 图)
找实体(Entity):
- 实体是现实世界的东西,比如“学生”、“班级”。
- 每个实体变成一张表(像 test 表、classes 表)。
- 实体有属性(比如学生有 id、name、class_id)。
找关系(Relationship):
- 实体之间的关系,比如“学生属于班级”(一对多)。
- 关系可能变成外键(class_id 关联 classes 表),或者独立表(多对多关系)。
画 E-R 图:
- 用矩形表示实体(Student、Class)。
- 用菱形表示关系(BelongsTo)。
- 用线连接,标明关系类型(1:1、1:N、N:M)。
转成表:
- 实体变成表,属性变成列。
- 关系用外键或中间表实现。
范式设计(基于规范化理论)
常见范式:
-
第一范式 (1NF)
:列不可再分,所有值是原子值。
- 比如不能存 name 列为 “enoch, lally”,得拆成两行。
-
第二范式 (2NF)
:非主键列完全依赖主键。
啥是依赖?
简单说:
- 依赖(Dependency)是数据库里的一种关系,描述“一个值能不能决定另一个值”。
- 就像你知道学生的 id,就能查到他的 name,这就是 name 依赖于 id。
啥叫“不完全依赖”?
-
如果主键是多列(复合主键),非主键列只依赖主键的一部分,就不满足 2NF。
-
比如主键是 (student_id, course_id),student_name 只依赖 student_id,这就是“不完全依赖”。
-
比如 test 表有 id(主键)、name、class_name,class_name 只依赖 class_id(不是主键),不满足 2NF。
-
解决:拆成两表,test (id, name, class_id) 和 classes (class_id, class_name)。
-
第三范式 (3NF)
:非主键列不能传递依赖。
- 比如 test 表有 id(主键)、class_id、class_name,class_name 依赖 class_id,class_id 依赖 id,不满足 3NF。
- 解决:把 class_name 放到 classes 表。
啥意思?
- 第三范式要求:表满足 2NF,并且非主键列之间不能有传递依赖。
- 传递依赖:如果非主键列 A 依赖主键,B 依赖 A,那 B 间接依赖主键,不满足 3NF。
ER相关概念
单值 (Single-Valued) 和 多值 (Multi-Valued)
-
单值
:一个实体在关系里最多对应 1 个对方实体。
- 比如:一个学生只能属于 1 个班级。
-
多值
:一个实体在关系里可以对应多个对方实体。
- 比如:一个班级可以有多个学生。
对方实体 (Other Entity)
啥意思?
-
对方实体是指关系中“另一边的实体”。
-
关系是两个实体之间的联系(二元关系),比如
Student和 Class 的关系
- 如果你在看 Student,那“对方实体”就是 Class。
- 如果你在看 Class,那“对方实体”就是 Student。
max-card(E, R)(最大基数)
-
啥意思
max-card(E, R)
表示实体 E 在关系 R 里最多能连几个对方实体。
- max-card(E, R) = 1:E 是“单值”的,一个 E 最多连 1 个对方。
- max-card(E, R) = N:E 是“多值”的,一个 E 可以连多个对方。
-
例子
- 关系:“学生属于班级”。
- 一个学生只能属于 1 个班级:max-card(Student, BelongsTo) = 1(单值)。
- 一个班级可以有多个学生:max-card(Class, BelongsTo) = N(多值)。
- 关系:“学生属于班级”。
min-card(E, R)(最小基数)
-
啥意思
min-card(E, R)
表示实体 E 在关系 R 里最少得连几个对方实体。
- min-card(E, R) = 1:E 必须至少连 1 个对方实体(强制)。
- min-card(E, R) = 0:E 可以不连对方实体(可选)。
-
之前讲过
max-card(E, R)
(最大基数),表示最多连几个:
- max-card 决定单值/多值(1 是单值,N 是多值)。
- min-card 决定强制/可选(1 是强制,0 是可选)。
二元关系转换
二元关系:两个实体间的关系(Student BelongsTo Class)。
转换规则:
- 一对多:多的一方加外键(Student 加 class_id)。
- 一对一:任选一方加外键,加 UNIQUE 约束(Person 加 idcard_id)。
- 多对多:建中间表(StudentCourse 存 student_id 和 course_id)。
强制/可选:
- 强制参与(min-card = 1):外键 NOT NULL。
- 可选参与(min-card = 0):外键允许 NULL。
弱实体
啥是弱实体?
简单说:
- 弱实体(Weak Entity)是不能独立存在的实体,必须依赖另一个实体(强实体)才能存在。
- 就像“订单明细”依赖“订单”:没有订单,就不会有订单明细。
比喻:
- 强实体像“人”,可以独立存在(有身份证号)。
- 弱实体像“人的手”,得依附于人(没有独立身份证号,得靠人来标识)。
关键特点:
- 弱实体没有自己的主键(Primary Key),需要借强实体的主键来一起组成自己的标识。
- 弱实体和强实体之间的关系通常是“一对多”(强实体是“一”,弱实体是“多”)。
啥是泛化层次结构?
简单说:
- 泛化层次结构(也叫“泛化/特化”或“继承层次”)是 E-R 图里的一种结构,用来表示实体之间的“父子关系”(类似于面向对象里的继承)。
- 泛化(Generalization):从多个子类型实体抽象出一个父类型实体(从下往上)。
- 特化(Specialization):把一个父类型实体细分成多个子类型实体(从上往下)。
- 就像“人”可以分成“学生”和“老师”,“人”是父类型(泛化),而“学生”和“老师”是子类型(特化)。
约束条件
- 完整约束 (Total Specialization)
- 父类型的每个实体必须属于某个子类型(不能只有父类型数据)。
- 用双线连接父类型和子类型。
- 比如:每个 Person 必须是 Student 或 Teacher。
- 部分约束 (Partial Specialization)
- 父类型的实体可以不属于任何子类型。
- 用单线连接。
- 比如:Person 可以只是 Person,不一定是 Student 或 Teacher。
- 不相交约束 (Disjoint
- 一个父类型实体只能属于一个子类型(Student 和 Teacher 不重叠)。
- 用三角形里写 d(disjoint)。
- 重叠约束 (Overlapping)
- 一个父类型实体可以属于多个子类型。
- 用三角形里写 o(overlapping)。
- 比如:一个 Person 可以既是 Student 又是 Teacher。
函数依赖
先复习:啥是函数依赖?
简单说:
- 函数依赖(Functional Dependency,简称 FD)是描述表里列之间的“决定关系”。
- 符号:X → Y,意思是如果 X 的值确定,就能唯一决定 Y 的值。
- 比如:id → name,知道 id 就能确定 name。
Armstrong 公理(三大基本规则)
Armstrong 公理是函数依赖的基础,总是成立。
(1) 自反律 (Reflexivity Rule)
- 规则:如果 Y 是 X 的子集(Y ⊆ X),那么 X → Y。
- 通俗解释:如果 Y 包含在 X 里,X 当然能决定 Y,因为 Y 就是 X 的一部分。
- 例子
- X = {id, name},Y = {id}。
- Y 是 X 的子集,所以 id, name → id 成立。
- 意义:这个规则很简单,主要用来推导“显然”的依赖。
(2) 增补律 (Augmentation Rule)
- 规则:如果 X → Y,那么对于任意属性集 Z,XZ → YZ(XZ 表示 X ∪ Z)。
- 通俗解释:如果 X 能决定 Y,那加点无关的属性 Z 进去,X 加 Z 也能决定 Y 加 Z。
- 例子
- 已知 id → name。
- 加个属性 Z = {class_id}。
- 根据增补律:id, class_id → name, class_id。
- 意义:可以“扩展”依赖,加入额外的属性。
(3) 传递律 (Transitivity Rule)
- 规则:如果 X → Y 且 Y → Z,那么 X → Z。
- 通俗解释:如果 X 能决定 Y,Y 能决定 Z,那 X 也能间接决定 Z。
- 例子
- 已知 id → class_id,class_id → class_name(因为 classes 表里有 class_id → class_name)。
- 根据传递律:id → class_name。
- 意义:用来发现间接依赖(也是第三范式要消除的传递依赖)。
基于三大公理,可以推导出一些更实用的规则,方便计算依赖。
(1) 并规则 (Union Rule)
- 规则:如果 X → Y 且 X → Z,那么 X → YZ。
- 通俗解释:如果 X 能决定 Y,也能决定 Z,那 X 能决定 Y 和 Z 一起。
- 例子
- 已知 id → name 和 id → class_id。
- 根据并规则:id → name, class_id。
- 推导:可以用增补律和自反律证明。
(2) 分解规则 (Decomposition Rule)
- 规则:如果 X → YZ,那么 X → Y 且 X → Z。
- 通俗解释:如果 X 能决定 Y 和 Z 一起,那 X 也能单独决定 Y 和 Z。
- 例子
- 已知 id → name, class_id。
- 根据分解规则:id → name 和 id → class_id。
- 推导:可以用自反律和传递律证明。
- 注意:分解规则是并规则的逆向。
(3) 伪传递律 (Pseudo-Transitivity Rule)
- 规则:如果 X → Y 且 WY → Z,那么 WX → Z。
- 通俗解释:如果 X 能决定 Y,而 Y 和 W 一起能决定 Z,那 X 和 W 一起也能决定 Z。
- 例子
- 已知 id → class_id,class_id, teacher_id → class_name。
- 根据伪传递律:id, teacher_id → class_name。
- 推导:可以用增补律和传递律证明。
闭包 (Closure) 的概念
-
啥是闭包:给定一组函数依赖 F 和属性集 X,X 的闭包 X⁺ 是 X 能决定的所有属性的集合。
-
咋算闭包:用 Armstrong 公理和推导规则,从 X 开始,逐步推导能决定的属性。
-
算法
(简单版):
- 初始化:X⁺ = X。
- 重复:对于每个依赖 U → V(U 是 X⁺ 的子集),把 V 加到 X⁺。
- 直到 X⁺ 不变。
-
例子
- 表:test (id, name, class_id)。
- 函数依赖:F = {id → name, id → class_id}。
- 求 id的闭包 id⁺
- 初始:id⁺ = {id}。
- id → name:加 name,id⁺ = {id, name}。
- id → class_id:加 class_id,id⁺ = {id, name, class_id}。
- 结束:id⁺ = {id, name, class_id}。
-
意义:闭包用来判断 X 能不能决定某个属性,或者是不是候选键。
覆盖和最小覆盖
1. 啥是覆盖 (Cover)?
简单说:
- 覆盖(也叫等价覆盖,Equivalent Cover)是另一组函数依赖 G,它和原来的函数依赖集 F 等价,也就是说,F 和 G 能推导出的所有依赖(闭包)是一样的。
- 符号:F⁺ = G⁺,表示 F 和 G 的闭包相同。
通俗解释:
- 就像你有两份“说明书”(F 和 G),内容不同,但能干的事(推导的依赖)完全一样。
- 覆盖的目标是找到一个更“简洁”的函数依赖集,但效果不变。
例子:
- 已知 F = {id → name, id → class_id, class_id → class_name}。
- 用传递律:id → class_id,class_id → class_name,推出 id → class_name。
- 构造一个新依赖集 G = {id → name, id → class_id, id → class_name}。
- 检查:F⁺ 和 G⁺ 都包含 {id → name, id → class_id, id → class_name},所以 G 是 F 的一个覆盖。
意义:
- 覆盖可以用来验证两个依赖集是否等价,或者简化依赖集。
2. 啥是最小覆盖 (Minimal Cover)?
简单说:
-
最小覆盖(Minimal Cover,也叫 Canonical Cover,记作 Fc)是函数依赖集 F
的一个“最简”覆盖,满足以下条件:
- 等价:Fc 和 F 的闭包相同(Fc⁺ = F⁺)。
- 每个依赖的右边(Y)只有一个属性(单属性依赖)。
- 没有冗余依赖(去掉任何一个依赖都不等价)。
- 没有冗余属性(每个依赖的左边和右边都不能再简化)。
通俗解释:
- 最小覆盖就像把说明书“精简”到最短:去掉多余的废话,每句话都很关键,但功能和原来一样。
- 目标是让函数依赖集尽可能简单,方便规范化设计(比如计算范式)。
求最小覆盖的步骤
给定函数依赖集 F,求最小覆盖 Fc 的步骤如下:
步骤 1:分解右边(单属性化)
- 把每个依赖的右边拆成单一属性(用分解规则:X → YZ 拆成 X → Y 和 X → Z)。
- 例子:
- F = {id → name, class_id; class_id → class_name}。
- 拆分:id → name, class_id 变成 id → name 和 id → class_id。
- 新 F = {id → name, id → class_id, class_id → class_name}。
步骤 2:去掉冗余属性(简化左边)
-
检查每个依赖的左边,看看能不能去掉某些属性。
- 对于每个依赖 X → Y,检查 X 里的每个属性 A,如果去掉 A 后(X - {A})依然能决定 Y(用闭包计算),就把 A 去掉。
-
例子:
-
假设有 F = {id, class_id → name, id → class_id, class_id → class_name}。
-
检查
id, class_id → name:
- 去掉 class_id,用 {id} 算闭包:id⁺ = {id, class_id, name}(包含 name),所以 class_id 冗余。
- 简化成 id → name。
-
新 F = {id → name, id → class_id, class_id → class_name}。
-
步骤 3:去掉冗余依赖
-
检查每个依赖,看看去掉后是否还等价。
- 对于每个依赖 X → Y,去掉它,计算剩余依赖的闭包,看 X⁺ 里是否还有 Y。
-
例子
-
当前 F = {id → name, id → class_id, class_id → class_name}。
-
去掉
id → class_id,剩余
{id → name, class_id → class_name}
- 算 id⁺:只有 {id, name},不包含 class_id,说明 id → class_id 不能去。
-
去掉
class_id → class_name,剩余
{id → name, id → class_id}
- 算 class_id⁺:只有 {class_id},不包含 class_name,说明 class_id → class_name 不能去。
-
没有冗余依赖,Fc = {id → name, id → class_id, class_id → class_name}。
-
最终最小覆盖:
- Fc = {id → name, id → class_id, class_id → class_name}。
再来个复杂点的例子
表:R(A, B, C, D)。 函数依赖:F = {A → BC, B → C, A → D}。
求最小覆盖:
- 分解右边:
- A → BC 拆成 A → B 和 A → C。
- B → C(已单属性)。
- A → D(已单属性)。
- 新 F = {A → B, A → C, B → C, A → D}。
- 去掉冗余属性:
- A → B:A 不可简化。
- A → C:A 不可简化。
- B → C:B 不可简化。
- A → D:A 不可简化。
- 没有冗余属性。
- 去掉冗余依赖:
- 去掉 A → C,剩余 {A → B, B → C, A → D}:
- 算 A⁺:A → B,B → C,所以 A⁺ = {A, B, C, D},包含 C,A → C 冗余。
- 新 F = {A → B, B → C, A → D}。
- 再检查:
- 去掉 A → B,剩余 {B → C, A → D},A⁺ = {A, D},不包含 B,不能去。
- 去掉 B → C,剩余 {A → B, A → D},B⁺ = {B},不包含 C,不能去。
- 去掉 A → D,剩余 {A → B, B → C},A⁺ = {A, B, C},不包含 D,不能去。
- 没有更多冗余依赖。
- 去掉 A → C,剩余 {A → B, B → C, A → D}:
- 最小覆盖:
- Fc = {A → B, B → C, A → D}。
求闭包的例子
关系模式的分解
简单说:
- 关系模式分解是把一个大表(关系模式)拆成多个小表的过程,目的是满足更高的范式(比如 3NF、BCNF),减少数据冗余和异常。
- 就像把一个大杂物间整理成几个小柜子,东西放得更有序,找起来也方便。
目标:
- 消除冗余(比如重复数据)。
- 避免更新异常(插入、删除、更新时不一致)。
- 保持数据的完整性(不能丢数据,不能改逻辑)。
关系模式分解的两个关键性质
分解时要保证两个性质,否则可能会丢数据或改逻辑。
1. 无损连接 (Lossless Join)
-
啥意思:分解后,通过连接(JOIN)能完全恢复原表的数据,不丢数据。
-
咋判断
:用函数依赖检查:
- 给定关系模式 R,分解成 R1 和 R2。
- 如果 R1 ∩ R2 → R1 或 R1 ∩ R2 → R2 成立(基于函数依赖),就是无损连接。
-
例子
:
- 原表 R(id, name, class_id, class_name)。
- 函数依赖:F = {id → name, class_id; class_id → class_name}。
- 分解:
- R1(id, class_id, class_name)。
- R2(id, name, class_id)。
- 交集:R1 ∩ R2 = {id, class_id}。
- 检查:id, class_id → class_name(成立),所以 id, class_id → R1,是无损连接。
2. 依赖保持 (Dependency Preservation)
- 啥意思:分解后,原来的函数依赖还能通过新表的依赖推导出来(不丢失约束)。
- 咋判断:
- 给定函数依赖集 F,分解成 R1 和 R2,分别算 F 在 R1 和 R2 上的投影(只包含 R1 和 R2 属性的依赖)。
- 如果这些投影能覆盖 F,就是依赖保持。
- 例子:
- 原依赖:F = {id → name, class_id; class_id → class_name}。
- 分解:
- R1(id, class_id, class_name):投影得 id → class_id, class_id → class_name。
- R2(id, name, class_id):投影得 id → name, class_id。
- 合并投影:{id → name, class_id; class_id → class_name},覆盖了 F,依赖保持。
完整性约束,视图,安全,系统目录
完整性约束 (Integrity Constraints)
-
啥意思:保证数据“正确”和“一致”的规则,约束是完整性约束的一部分。
-
种类:
- 实体完整性:主键不能重复且不能为 NULL(比如 id)。
- 参照完整性:外键值必须存在(比如 class_id 必须在 classes 表里)。
- 用户定义完整性:业务规则,比如 CHECK (class_id > 0)。
-
例子:class_id 不能为 NULL(如果强制参与),且必须在 classes 表里有对应值。
-
约束(Constraint)是数据库里的一套规则,用来限制表里的数据,确保数据的正确性和一致性。
-
就像学校的规章制度,约束让数据“守规矩”,避免乱七八糟的情况。
常见约束类型:
- 主键约束 (PRIMARY KEY):确保每行唯一且不为空(比如 id)。
- 唯一约束 (UNIQUE):列值不能重复(但可以为空)。
- 非空约束 (NOT NULL):列不能为 NULL。
- 外键约束 (FOREIGN KEY / REFERENCE):保证引用值存在。
- 检查约束 (CHECK):限制值的范围或条件。
- 默认约束 (DEFAULT):给列设置默认值。
CREATE TABLE 表名 (列名 数据类型 CHECK (条件),...
);
或单独加
ALTER TABLE test
ADD CONSTRAINT chk_name_length CHECK (LEN(name) > 2);
外键约束语法
CREATE TABLE 表名 (列名 数据类型,CONSTRAINT 约束名 FOREIGN KEY (列名) REFERENCES 另一表(列名),...
);
discnt real constraint discnt_max check (discnt<=15.0)
discnt的类型是(real),加一个check限制discnt
视图 (Views)
- 啥意思:视图是一个“虚拟表”,基于实际表(物理表)的查询结果,简化数据访问。
- 作用:
- 隐藏复杂查询:用户只看到需要的部分。
- 保护数据:限制用户访问某些列。
CREATE VIEW student_class_view AS
SELECT t.name, c.class_name
FROM test t
JOIN classes c ON t.class_id = c.class_id;
SELECT * FROM student_class_view;
安全 (Security)
-
啥意思:控制谁能访问或修改数据库里的数据,保护敏感信息。
-
方法:
-
用户权限:用 GRANT和 REVOKE
分配权限。
-
比如只允许某用户读 test表:
GRANT SELECT ON test TO user1;
-
禁止更新:
REVOKE UPDATE ON test FROM user1;
-
-
角色:给一组用户分配角色,比如 db_reader角色。
CREATE ROLE db_reader; GRANT SELECT ON test TO db_reader;
-
视图保护:用视图限制可见数据(比如上面的 student_class_view 只暴露 name 和 class_name)。
-
-
例子:
-
假设 class_name是敏感数据,只允许管理员看:
GRANT SELECT ON classes TO admin; DENY SELECT ON classes TO public;
-
数据库安全的四个等级
1. 系统级安全 (System-Level Security)
-
啥意思:
- 系统级安全是最外层的防护,关注数据库运行的整个系统环境(操作系统、网络等)。
- 目的是防止未经授权的用户进入数据库所在的系统。
-
咋做:
-
操作系统权限
:限制谁能登录数据库服务器。
- 比如:只有 DBA(数据库管理员)能登录服务器,其他人不能。
-
网络安全
:保护数据库的网络访问。
- 用防火墙限制访问(比如只允许特定 IP 访问 SQL Server 的 1433 端口)。
- 用 SSL/TLS 加密网络传输,防止数据被拦截。
-
身份验证
:验证用户身份。
- SQL Server 支持两种身份验证:
- Windows 身份验证:用 Windows 用户登录(更安全)。
- SQL Server 身份验证:用用户名和密码(比如 sa 用户)。
- SQL Server 支持两种身份验证:
-
2. 数据库级安全 (Database-Level Security)
- 啥意思:
- 数据库级安全关注整个数据库的访问控制,确保只有合法用户能访问数据库。
- 咋做:
- 用户管理:创建数据库用户,分配权限。
3. 对象级安全 (Object-Level Security)
-
啥意思:
- 对象级安全关注数据库里的具体对象(表、视图、存储过程等),控制用户对这些对象的操作。
-
咋做:
-
权限分配:用 GRANT和 REVOKE控制权限。
-
比如只允许 user1 读 test 表:
GRANT SELECT ON test TO user1;
DENY UPDATE ON classes TO user1;
-
-
限制用户只能访问某些行和对某些列进行加密等操作,一定程度上保护数据安全
系统目录 (System Catalogs)
- 啥意思:系统目录是一组特殊的表,存储数据库的“元数据”(Metadata),也就是数据库里所有对象(表、视图、约束等)的描述信息。
- 作用:
- DBA 可以查系统目录,了解数据库结构。
- 比如:有哪些表、表的列、约束、权限等。
- 在 SQL Server 中:
- 系统目录包括 sys.tables、sys.columns、sys.foreign_keys 等。
练习题
文件系统和DBMS的四个主要区别
方面 | 文件系统 | DBMS |
---|---|---|
数据组织 | 文件分散,无结构化关系 | 表结构,有关系(外键等) |
一致性完整性 | 无约束,易不一致 | 有约束、事务,保证一致性 |
访问查询 | 手写代码,效率低 | SQL 查询,高效有优化 |
并发安全 | 无并发控制,安全粗糙 | 并发控制强,细粒度安全 |
解释物理独立性,以及它在数据库系统中的重要性。
物理独立性是指应用程序和用户查询(逻辑层)不受数据库底层物理存储结构(物理层)变化的影响。
也就是说,数据库的存储方式(比如文件、索引、磁盘分布)变了,应用程序和 SQL 查询不用改,照样能正常工作。
数据库系统通常分三层(ANSI-SPARC 架构):
- 外部层(External Level):用户看到的数据视图(比如视图)。
- 逻辑层(Conceptual/Logical Level):表的逻辑结构(比如 test 表的列和关系)。
- 物理层(Physical Level):底层存储(文件、索引、分区)。
物理独立性是逻辑层和物理层之间的隔离。
其实就是逻辑层(用户查询,表结构)不受物理层(存储,索引)的变化迎新
重要性:
- 灵活性:随便更改物理存储位置,用户不受影响
- 省成本:应用不用改
- 性能好:支持底层优化
- 可扩展:数据量大也可以轻松调整
找出候选键
A | B | C | D | E |
---|---|---|---|---|
a1 | b1 | c1 | d1 | e4 |
a1 | b1 | c2 | d2 | e3 |
a1 | b2 | c3 | d1 | e1 |
a1 | b2 | c4 | d2 | e2 |
候选键都是有潜力成为主键的,必须要最简洁,可以用一列就绝对不用两列