###### tags: `learn` `DataBase` # 数据库系统 # :::success 关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。除了主流的关系型数据库外的数据库,都认为是非关系型。 关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。 在扩展方式上,关系型数据库更易于扩展(就像你写程序一样,把文件按模块写好以关系调用一定比你都一股脑丢一个模块里好修改) ::: ---------- ## 第二周 ## **数据库系统的结构:** 用户层次:用户能看到与处理的数据 概念层次:从全局角度理解/管理的数据,含相应的关联约束 物理层次:存储在介质上的数据,含存储路径,存储方式与索引方式等 对于数据,我们看到的叫数据或视图,实际上视图与数据是模式的一种展现模式。 - 外模式:对用户能看到与处理的数据的结构描述 - 概念模式:从全局角度理解管理的数据的结构描述 - 内模式:存储在介质上的数据的结构描述,含存储路径,存储方式与索引方式等 ### 两层映像 ### 1. E-C Mapping:外模式至概念模式的映像 2. C-I Mapping:概念模式至物理模式(内模式)的映像 ### 独立性 ### 1.逻辑数据独立性:当概念模式发生变化时,无需修改外模式,只修改EC映像即可。 2.物理数据独立性:当内模式发生变化时,无需修改逻辑模式,只修改CI映像即可。 ### 数据模型 ### 数据模型:规定模式统一描述方式的结构(决定数据结构的模型?) 常见的数据模型: - 关系模型:表 - 层次模型:树 - 网络模型:图 ## 第三周:关系模型 ## 关系模型:一个经典数据模型。 一个关系就是一个table,关系模型由table组成。 关系模型的操作是一次一集合的操作!而非关系模型通常是一次一记录的操作。 ### 什么是关系? ### table:一个表我们就称为一个关系。 首先,每一列的取值范围都是一个域,反过来,域就是一列值的集合。 元组:每一行的不同取值的所有可能组合。(笛卡尔积其实就是组合的总情况a1*a2*a3...。。。) 关系:一组域的笛卡尔积的子集(就是取值的某种或复数种组合) 关系模式:对应的就是变量与其变量取值 ### 关系的特性 ### 1. 同质:每一列的分量来自同一个域有共同性质 2. 不同的列也可来自于同一个域 3. 凭借列名和行的值来区分行与列 4. 关系的任意两个元组不可以完全相同(无重复性) 5. 关系内的属性不可再分!!!!! ### 候选码/候选键 ### 若关系中的一个属性组,其值能唯一标识一个元组,且若去掉该组中任意一个属性就不再具有这个性质,那么这样的一个属性组被称为候选码。 (比如,学生表里,学号就可以是候选码(一般会是主码),可以区分所有学生!) 候选码很重要,尽量不要修改因为修改了你整个数据库都需要跟着变动。 ### 主码/主键 ### 当有多个候选码时,选定一个作为主码!就最关键的意思 包含在任一候选码的属性称为主属性,其他的成为非主属性 ### 外码/外键 ### 如果R中某个属性组不是R的候选码,但它是另一个关系S的候选码,就称为外码。 **两个关系通常靠外码链接!!!!!!!** ## 关系模型的完整性 ## ### 1.实体完整性 ### - 关系中主码中的属性值不能为空值(不知道或无意义的值)! ### 2.参照完整性 ### - 关系中的外码可以为空值,但是不能取其作为主码时的域以外的值! ### 3.用户自定义完整性 ### - 用于可以针对应用环境来定义约束条件(比如性别定义只能填入男/女!) # 第四周:关系代数 # ## 关系代数 ## **关系操作**: (1).集合操作:并,交,差,笛卡尔积 (2).纯关系操作:投影,选择,连接,除 **关系代数**: 用于表达关系操作! ### 并相容性(并,差,交的操作要求) ### 参与运算的两个关系以及相关属性之间应有一定的对应性,可比性或意义关联性!! ## 集合操作 ## #### 并操作(Union) #### 和数学里一样,并表示合并两个关系。 #### 差运算 #### 相当于集合里的 A - AB! #### 广义笛卡尔积 #### 记作R x S,表示R与S所有可能的拼接(就是把R和S里所有元素放一起后随机排列的所有可能的集合) 我的感觉就是,使用pandas拼接两个无共同部分的数据表(横向)这样。 ## 纯关系操作 ## #### 选择操作 #### 从R里选出满足条件的元组(select) --- 啊这...不会和python一样的吧 #### 投影操作 #### 就相当于...对表格进行切片操作,生成新的关系 **注意** 选择操作和投影操作有什么差别? - 选择操作选出来的是行, 投影操作是根据列进行选择。 ## 扩展操作 ## #### 交运算 #### 求关系的交!!!(参照集合的交集 ∩ ) 通过差可以实现交: R∩S = R - (R-S) #### θ-连接 #### 就是对表进行选择条件的连接!在乘积运算基础上进行选择运算 重点在于这个符号是横向对三角,在符号下面书写选择条件 用python写的话会先连接表(连列),再筛选条件(选行),但是数据库语言可以直接一个符号实现! #### 等值连接 #### 是一种特殊的θ-连接操作,仅连接两个关系值相等的部分(实际上就是,你在第一个表中选定某个属性,然后第二个表中也选择一个属性,然后以这两个属性为合并键(也就是把他俩当成一个属性,比如当成1的外键且2的主键)并且选择只留下两属性值相等的部分!!!、 #### 自然连接 #### 在笛卡尔积中存留属性相同且值相等的部分。(需要满足,两个属性名相同且属性值完全相等!) 实际上自然连接是最普遍的连接,我们平常用的很多也都是! 就是你在连接两个不同的表时,会选取两个表中的共有的特征(属性)并且以那个属性为关键,选择保留两个表共同的部分(也就是属性值相同的部分) ## 复杂扩展操作:除操作 ## **用于解决:‘查询全部的/所有的...’** 比如: 表1:A,B,C三个特征,其中C特征有5个数,1,1,2,3,4 表2:只有C特征,值有,1,3 那么1对2的除操作,我们会得到: - 只带有A,B特征的,该A,B特征与1中的1,3组合在一起的元组都在对应表1中能找到的新表。 ## 复杂扩展操作:外连接操作 ## 对于长短表连接,短的那个表自然连接时候在数据库中消失(不被记入)...虽然在python内连接的话会默认给值,但是这里不会的样子。 为了防止这个,就产生了外连接操作,对于属性值不全的项目,会默认匹配‘?’表示空值(**注意,空值不是空,是不知道或者无意义的值!**) # 第五周:关系演算 # ## 关系元组演算 ## 以**元组为基本变量**,就是集合的运算。(这个运算以表的全部特征为变量,需自己指定需要运算的变量) ## 存在量词与全称量词 ## 首先,存在与全称,就是数学符号的存在与对于全部,用于验证是否满足条件 ### 等价性变换 ### 善用补集符号! ## 元祖演算 ## 关系元组演算是以元组变量作为谓词变量的基本对象 基本形式 { t | P(t) } P(t)可以是如下三种形式之一的原子公式: t ∈ R t 是关系 R 中的一个元组,例如: { t | t∈ Student} s[A] θ c 元组分量s[A]与常量 c 之间满足比较关系. θ:比较运算符,<, <=,=, <>,>, >= 例如: { t | t∈Rt [Sage ] <= 19∧ t [Sname ] = ‘张三’ } s[A] θ u[B] s[A] 与 u[B] 为元组分量,A和B分别是某些关系的属性,他们之间满足比较关系θ. 例如: { t | t∈Student∧ ∃ (u ∈Student) ( t [Sage ] > u [Sage ] ) } “检索出年龄不是最小的所有同学” ![](https://img-blog.csdnimg.cn/20200807160356786.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) 存在量词∃与全称量词∀ ![](https://img-blog.csdnimg.cn/20200807160540194.png) ![](https://img-blog.csdnimg.cn/20200807160558201.png) ---------- ## 关系域演算 ## 以**域为基本变量**,和元组演算相似(这个运算是直接将各特征作为变量 进行运算!) 域演算过程性很差,适用于用户进行查找。 ## 域演算 ## 关系域演算公式的基本形式: { < x1 , x2 , … , xn > | P ( x1 , x2 , … , xn ) }其中, xi 代表域变量或常量, P为以xi为变量的公式。公式P可以递归地进行构造: 三种形式的原子公式是公式 < x1 , x2 , … , xn > ∈ R。 其中xi 代表域变量或常量, 表示由域变量构成的< x1 , x2 , … , xn >是属于关系R的x θ c。 其中,域变量x与常量c之间满足比较关系θ 。θ:比较运算符 <, <=, =, <>, >, >=x θ y。其中,域变量x与域变量y之间满足比较关系θ。 例子 ![](https://img-blog.csdnimg.cn/20200807160953541.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ![](https://img-blog.csdnimg.cn/2020080716100564.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ## QBE(基于域演算的查询方式) ## 按示例查询:QBE(很显而易懂的查询) 基本部分 关系名区:用于书写欲待查询的关系名 属性名区:用于显示对应关系名区关系的所有属性名 操作命令区:用于书写查询操作的命令 查询条件区:用于书写查询条件 ![](https://img-blog.csdnimg.cn/20200807161116906.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ### QBE的操作命令 ### 更新操作: ![](https://img-blog.csdnimg.cn/20200807161252432.png) 查询操作: ![](https://img-blog.csdnimg.cn/20200807161315858.png) QBE的查询条件----不同属性上的与条件 QBE不同属性上的与条件可以写在同一行中 ![](https://img-blog.csdnimg.cn/20200807161825200.png) ### QBE的查询条件----示例元素与投影 ### 条件 θ 参量中的参量也可以是域变量,用任何一个值(不必是结果中 的值)带有下划线表示,被称为示例元素. 示例元素下划线上面的值不 起作用,被当作域变量名称来对待,只用于占位或是连接条件。不带下 划线的则是构成实际条件一部分的值。 ![](https://img-blog.csdnimg.cn/20200807162040586.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ### QBE的查询条件----用示例元素实现‘与’运算和‘或’运算 ### 当书写 ∪ 条件(或运算)时,可以采用在多行书写,然后在打印命令后使用不 同的示例元素来表征,如下图, 一行写为P.X, 一行使用P.Y ![](https://img-blog.csdnimg.cn/20200807162137728.png) 如果一批 ∩ 条件分多行书写,则相互存在∩关系的行要采用相同的示例元素 ![](https://img-blog.csdnimg.cn/20200807162236129.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ### QBE的查询条件----相当于括号的条件表示 ### ![](https://img-blog.csdnimg.cn/20200807162339484.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) ### QBE的查询条件----用示例元素实现多个表的连接 ### 当检索涉及多个表时,可利用同一连接条件使用相同的示例元素,来实现多个表的连接. ![](https://img-blog.csdnimg.cn/20200807162426806.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1MTA5ODcw,size_16,color_FFFFFF,t_70) 总结: ![](https://img-blog.csdnimg.cn/202008071630585.png) ## 关系运算的安全性 ## 安全:不产生无限关系和无穷验证的运算是安全的。 关系代数是安全的。但关系演算不一定是安全的。 ## 第六周:SQL语言 ## ### 利用SQL建立数据库 ### **DDL:数据定义语言** - **创建数据库** -- CREATE Database 数据库名 - **创建DB中的Table** -- CREATE Table 表名(列名 数据类型 [键类型]) - 键类型: - primary key:主键(只能有一个) unique:候选键 Not null:非空约束,不允许空值类型 - **向表中添加元组(使用DML)** - 追加新元组:insert: - insert into 表名[(列名[,列名]...] - values (值 [,值,...); - 查询:select: - select 列名 [列名, ...] - from 表名 - where 条件 - order by 顺序 - limit 限制条数 - 多表联合查询: - select 列名... - from 表名1 (as 新名字), 表名2 (as 新名字) - where 条件 - 删除:Delete: - delete from 表名 (where 条件) - 更新 Update: - update 表名 - set 列名 = 表达式 - where 条件 - 修正数据库 alter: - alter table 表名 - (add ()) 新增列 - (drop ()) 删除约束 - (modify ()) 修改定义 - 撤销修改:drop: - drop table 表名 - 与delete from 不同,是直接删除掉了表的格式,表内元组以及相关结构内容! ## 第七周:sql复杂查询 ## ### 1. (NOT) IN 子查询 ### 子查询:出现在where子句中的select语句被称为子查询,子查询返回一个集合,通过与这个集合的比较确定另一个查询集合。 表达式: [NOT] IN (子查询) eg. SELECT * FROM stu WHERE sname in ('z', 'q') ### 2. θ some/θ all 子查询 ### 表达式:θ some (子查询) θ all (子查询) 注意:这里的θ指>,<,=等比较运算符。 eg.找出工资最低的教授姓名 SELECT name FROM teacher, WHERE salary <= all (SELECT salary FROM teacher) ### 3. (NOT)EXIST 子查询 ### - 这里我贼几把晕 表达式: [NOT] EXIST (子查询) ### 结果计算 ### 就是可以在SELECT里输入公式! #### 聚集函数 #### SQL提供了五个聚集函数(agfunc):COUNT, SUM, AVG, MAX, MIN 注意:这些聚集函数不可以用在 WHERE 句里! #### 分组聚集计算与分组过滤 #### 分组条件:GROUP BY 语法:SELECT FROM WHERE ORDER BY GROUP BY 条件 [HAVING 过滤条件] ### 并,交,差的处理 ### 并运算 UNION, 交运算 INTERSECT, 差运算EXCEPT 基本语法:子查询{ UNION[ALL] | INTERSECT[ALL] | EXCEPT[ALL] 子查询} eg. 选择学过02或者03课程的学生学号: 1. SELECT S# FROM sc WHERE c# IN ('02','03') 2. SELECT S# FROM sc WHERE c# = '02' or c# = '03' 3. SELECT S# FROM sc WHERE c# = '02' UNION SELECT S# FROM sc WHERE c# = '03' #### 并交差 ALL 与 并交差的区别??? #### 若要保留重复的元组!!!则用ALL,不带ALL则会去掉重复元组 ### 空值处理 ### 空值:不知道或无意义的值 检测空值: IS [NOT] null eg. 找出年龄值为空的学生姓名 SELECT Sname FROM Student WHERE Sage IS null #### DBMS的空值处理 #### - 除 IS [NOT] NULL 之外,空值不满足任何查找条件 - 如果NULL 参加算术运算,则该算式表达式的值也为NULL - 如果NULL 参与比较运算,结果视为false - 如果NULL 参与聚集运算,则除 COUNT(*)之外的操作都会忽略NULL ### 连接操作 ### SELECT * FROM 表1 [NATURAL] [INNER | {LEFT|RIGHT"FULL}[OUTER]] JOIN 表2 {ON 连接条件} WHERE 检索条件 ### SQL视图 ### 外模式对应数据就是视图! 定义视图:(先定义,再使用) 语法:CREATE VIEW 视图名【列名...】 AS 子查询 【...】 eg. CREATE VIEW compstud AS (SELECT * FROM student) ## 八周:SQL语言与数据库完整性与安全性 ## 数据库完整性:DB在任何情况下都应该是正确,有效且一致的。 - 广义完整性:并发控制,安全控制,DB故障恢复等 - 狭义完整性:语义完整性 ### 数据库完整性分类 ### **按约束对象分类** - 域完整性约束:对某一列的值更新时施加的约束条件,这是独立进行的。 - eg. 要求表内的age>18 - 关系完整性约束:施加于关系/table上的,对更新的某一元组世家的约束条件,这是联合进行的。 - eg. 要求每个人的总薪资除以年龄 大于 200, salary/age > 200 **按约束来源分类** - 结构性约束:来自模型的约束,如主键不允许重复与空值等。 - 内容性约束:关于内容的约束,如用户自定义完整性等。 - eg. age > 15 **按约束状态分类** - 静态约束:要求DB任何时候都满足的约束,如age>15 - 动态约束:要求DB改变状态时应满足的约束,如规定工资只能升不能降。 ## 利用SQL语言实现数据库的静态完整性 ## SQL支持如下约束: 静态约束: - 列完整性---域完整性约束 - 表完整性---关系完整性约束 动态约束: - 触发器 **静态约束**: 使用 CREATE Table 定义:列完整性与表完整性。 **1. 列约束** 列约束Col_constr示例: CREATE TABLE student (S# char(8) not null unique, Sex char(2) constraint ctssex check (Sex = '男' or Sex = '女') Sage check(Sage >= 1 and Sage <= 30) 上面这三个列约束,分别定义了S#的唯一性与非空,以及性别的约束ctssex函数,每个新元组检查性别是否为男或女。注意,此处的ctssex为什么要命名呢?因为有名字的约束可以做到方便删除,或者赋给其他列使用! 最后一个约束则是没有命名的约束,也可以由此发现,约束不一定需要命名! ---------- **2.表约束** 表约束Table_constr示例: CREATE TABLE student (S# char(8) not null unique, Sex char(2) constraint ctssex check (Sex = '男' or Sex = '女') Sage check(Sage >= 1 and Sage <= 30), primary key(S#), constraint ctcc check (Sage*Salary > 3000) 最后一排则为表约束。放在最后。 撤销约束:对于命名约束可以撤销。方式在下: ALTER TABLE 表名 DROP CONSTRAINT ctssex 这样就删除掉了上面的列约束ctssex 追加约束:同上的操作方式: ALTER TABLE 表名 ADD CONSTRAINT 约束名 check (Sage >= 18) ### 断言:表达希望数据库总满足的条件 ### 和py里的assert很像嘛! 语法:(慎用) CREATE ASSERTION 断言名 CHECK (条件) eg. CREATE ASSERTION money_count CHECK (sage <= salary)、 **动态约束** 触发器Trigger:在某种情况下会被触发 语法: CREATE TRIGGER 触发器名 BEFORE | AFTER {INSERT | DELETE | UPDATE ...} 触发器示例: 1. 设计一个触发器使对Teacher表更新时,工资只能升不能降。 CREATE TRIGGER salary_control BRFORE UPDATE OF salary ON teacher REFERENCING NEW x OLD y FOR EACH ROW WHEN (x.salary < y.salary) BEGIN raise_application_error('Invalid salary on update') END; 2. 设计一个触发器,使学生没多上一门课,触发器自动计数加一。 CREATE TRIGGER sum_class AFTER INSERT ON sc REFERENCING NEW ROW newclass FOR EACH ROW BEGIN UPDATE sc SET numberofclass = numberofclass + 1 WHERE S# = newclass.S# END; ## 数据库安全性概念及分类 ## 数据库安全性:DBMS应保证的数据库的免受非法,非授权用户的使用,更改,泄露或破坏。 **安全性机制** - 自主安全性机制:通过权限在用户间传递,使用户自主管理数据库安全性。 - 强制安全性机制:通过对数据与用户强制分类,使得不同类别的用户能够访问不同类别的数据。 - 推断控制机制 - 数据加密等 ### 自主安全性机制 ### 对用户授权。DBMS允许用户定义安全性控制规则。 访问规则:AcsessRule = (S, O, t, P) S: 请求主体(用户) O: 访问对象 t: 访问权利 P: 谓词 释义:S用户对O这个访问对象,在满足P这个条件时拥有t的访问权利。 - 按名控制:存储矩阵。对主体/对象及访问权利使用矩阵存储。 - 视图控制:定义不同视图给不同的用户使用!! #### SQL语言实现自主安全性 #### SQL中等数据管理员级别:超级用户(DBA) - 账户级别 - 关系级别 级别1: Select:读 级别2: modify:更新(INSERT, UPDATE, DELETE) 级别3: create:创建(CREATE, ALTER, DROP) 高级自动包含低级权利。 **授权命令: GRANT** eg. GRANT All Priviledges ON employee TO me; GRANT SELECT ON empv2 TO coder; GRANT SELECT ON empv3 TO public; **收回授权命令:REVOKE** eg. REVOKE SELECT ON employee FROM coder; **授权传播** eg. GRANT SELECT ON employee TO coder WITH GRANT OPTION; 这样就给予了对象向其他人授权的权利。 但这东西好像不是很安全。到处给人钥匙不大好! #### 强制安全性机制 #### 通过对数据对象进行安全性分级,也对用户进行这样的划分,从而实现不同级别用户访问不同级别数据的机制。 ## 第九周:嵌入式SQL语言 ## **交互式SQL语言的局限:** 可以实现查询,但是: 1. 使用者角度:难以书写复杂SQL语言 2. SQL角度:复杂检索难以用一句交互式SQL完成 **嵌入式SQL: 高级语言+SQL语言!** 高级语言: C++, Java等,称为宿主语言。 示例: EXEC SQL SELECT sname INTO :vsname FROM student WHERE sname <> '张三' 注: 1. EXEC SQL: 为引导SQL语句,提供该语句给了C编译器。 2. INTO: 指出接受SQL语句检索结果的程序变量(指将检索结果赋予给了 vsname 这个变量!!!! 高级语言通过嵌入式SQL连接到DBMS ### 嵌入式SQL ### 问题1 ~ 8: #### 问题1: 高级语言如何与数据库1连接 & 断开连接? #### SQL标准的连接建议语法: EXEC SQL CONNECT TO target_server AS connect_name USER user_name; 或 EXEC SQL CONNECT TO default; ================================= 断开连接语法: EXEC SQL DISCONNECT connect_name; 或 EXEC SQL DISCONNECT CURRENT #### 问题2: 高级语言的程序变量如何传递给嵌入式SQL语句? #### 示例SQL语句: EXEC SQL SELECT name, age INTO :vSname, vSage FROM student WHERE name = :specName; 会发现很多地方加了冒号 **原因**:加了冒号的都是高级语言程序里的变量,这样便于区分SQL的属性与高级语言的变量! 这些变量需要特殊声明,如: EXEC SQL BEGIN DECLARE SECTION: char vSname[10], specName[10] = '张三': int vSage EXEC SQL END DECLARE SECTION; **那么,相对于交互式语言,它有什么优势呢??** 观察上面语句,我们将高级语言的变量 specName 赋值了 ’张三‘, 那么, 想要更改搜索条件时,我们只去更改变量值即可,比较灵活。(就相当于写函数调用更方便吧) - 也就是说, 变量的传递需要先进行声明, 再使用!!! #### SQL的事务处理 #### 事务: - (程序角度)一个存取或改变数据库内容的程序的执行;或一条,多条的SQL语句的一次执行。 - (微观或DBMS角度)数据库管理员提供的, 进行数据操作的手段。 事务的开始与结束: BEGIN TRANSACTION (可不写) 语句... 语句... . . . EXEC SQL COMMIT work | EXEC SQL ROLLBACK work (必须) END TRANSACTION (可不写) 通过SQL执行的提交与撤销: 提交语句: EXEC SQL COMMIT work --- 产生一个新的事务 撤销语句: EXEC SQL ROLLBACK work --- 取消一个事务 **事务的特性:ACID** - 原子性Atomicity: 事务的一组更新操作原子不可分;要么都做,要么都不做。 - 一致性Consistency: 事务的操作状态应正确且符合一致性规则。 - 隔离性Isolation: 多个并发执行事务间互不影响。 - 持久性Durability: 已提交事务的影响时持久的,且被撤销事务的影响是可恢复的。 ---------- #### 问题3: SQL语句如何执行 #### #### 问题4: 如何将SQL检索结果返回宿主程序处理 #### #### 问题5: 静态SQL,SQL语句中的常量更换为变量 #### ### 答案在游标! ### 游标(cursor):在检索多行结果时使用。 - 游标是指向结果集合的指针! - 通过这个指针的移动,可以实现每次读取并处理一行的任务。 - 读取语句: FETCH INTO ... 一直读取到EOF(没有记录为止) **游标的使用:** 定义 --- 执行 --- 逐条处理 --- 关闭 定义: EXEC SQL DECLARE 游标名 CURSOR FOR SELECT 选择内容 FROM 表 WHERE 条件; 执行: EXEC SQL OPEN 游标名 逐条处理(配合循环): EXEC SQL FETCH 游标名 INTO 主程序变量 关闭: EXEC SQL CLOSE 游标名 实际上就是创建一个指针,指向多行查询结果的表头,并将当前行内容读取; 在使用时候直接如逐条处理, 将游标内容传递给主程序变量即可! #### 可滚动游标 #### 标准游标的话,由于是向下移动,一条记录只能够被访问一次!再次访问需要关闭后重新打开。 而, **ODBC**支持了滚动游标! EXEC SQL DECLARE 游标名 [SCROLL] CURSOR ... EXEC SQL OPEN 游标名 EXEC SQL FETCH [NEXT | PRIOR | FIRST | LAST | 【ABSOLUTE | RELATIVE】 位置] FROM 游标名 INTO 主程序变量 **注:** - SCROLL 不写就是普通游标! - 前四种 (NEXT | PRIOR | FIRST | LAST): - 相对于当前位置的变化 - 后面的 (【ABSOLUTE | RELATIVE】 位置): - ABSOLUTE: 绝对位置(全局的几号) - RELATIVE:相对位置(前/后几号) ### 利用游标与程序对数据库进行增删改操作! ### **删除** - 查找删除 - 定位删除 二者的选择与区别写在下: EXEC SQL DELETE FROM 表名【列名】 WHERE 查找删除条件 search_condition | 定位删除(当前:CURRENT OF 游标名) eg. 查找删除 EXEC SQL DELETE FROM student[sname] WHERE sname = '张三' eg. 定位删除 EXEC SQL DECLARE 游标 CURSOR FOR SELECT id FROM customer c WHERE c.city = 'harbin' AND NOT EXISTS (SELECT * FROM order o WHERE o.id = c.id) FOR UPDATE OF id; EXEL SQL OPEN 游标 WHILE(True){ EXEC SQL FETCH 游标 INTO :cust_id; EXEC SQL DELETE FROM customer WHERE CURRENT OF 游标;} **更新** 同删除,分查找与定位! EXEC SQL UPDATE 表名【列名】 SET columnname = expr[, columnname=expr...] [WHERE 查找条件]| WHERE CURRENT OF 游标名; **插入** 只有一种早就认识的类型了! EXEC SQL INSERT INTO 表名(列名...) VALUES (值...) ### 状态捕获及错误处理 ### #### 问题6: 主程序如何知道SQL语句的执行情况,如是否发生错误? #### **嵌入式SQL中的状态捕获与处理有三部分:** - 设置SQL通信区: - EXEC SQL INCLUDE SQLCA; - 导入必要的SQLCA - 设置状态捕获语句: - EXEC SQL WHENEVER sqlerror GOTO report_error; - 捕获到 sqlerror 错误时, 执行report_error的处理 - 设置状态处理语句: - report_error: EXEC SQL ROLLBACK; - 处理语句书写 **SQLCA**:是一个被声明过的C语言结构形式的内存信息区,其中的成员变量用于记录SQL语句执行状态,便于主程序读取与处理。 **它是DBMS(执行SQL语句)与主程序间交流的桥梁之一!!!** 状态捕获语句: EXEC SQL WHENEVER condition action; - condition:设置的错误条件, 满足则执行action; - action:采取动作: - CONTINUE: 忽略错误继续执行 - GOTO 标号:转移到标号语句处理(一般是处理语句) - STOP:终止程序 - DO / CALL 函数:调用主程序函数处理,处理完后继续运行之后语句 处理无限循环: 放置在状态处理语句中,以便于在处理完回到后续中产生无限循环! handle_error: EXEC SQL WHENEVER sqlerror CONTINUE; 一定要加这句, 没有的话容易产生无限循环!!! . . . DBMS 记录状态的方法: - sqlcode: - 0表示正常运行, 小于零表示有错误, 大于零表示无错但有Warning - 只返回是否出错,不返回错误类型 - sqlca.sqlcode: - sqlstate 根据错误条件处理错误:书写条件与行为action在程序中。 ## 第十周:嵌入SQL之动态SQL ## #### 动态SQL #### 静态SQL: 语句已经在程序中按要求写好,只需要把参数通过变量(高级语言程序语句中不带冒号)传递给嵌入式SQL语句(在嵌入式语句中带冒号) eg. SpecName = 'zhangsan' exec sql select Sno, Sname, Sclass into :vSno, :vSname, :vSclass from Student where Sname = :SpecName; 动态SQL:SQL语句可以在程序中动态构造形成一个字符串,再交给DBMS执行,此时仍可以传递变量 eg. exec sql include sqlca; exec sql begin declare section: char user_name[] = 'Scott'; char user_pwd[] = 'tiger'; char sqltext[] = 'delete from customers wgere cud = 'c006'; ---- 这里!! exec sql end declare section; int main() { exec sql whenever sqlerror goto report_error; exec sql connect :user_name identified by :user_pwd; exec sql execute immediate :sqltext; ---- 这里!! exec sql commit release; return 0; report_error: print_dberror(); exec sql rollback release; return 1 } **为什么要掌握动态SQL?** 可以通过动态SQL设计简易GUI界面!(QBE) #### 数据字典与SQLDA #### 数据字典/系统目录:系统维护的表与视图的集合 元数据:关于数据的数据,数据字典就是一种元数据。 **数据字典构成:** 1. 与关系相关的信息: - 关系名 - 关系的属性名及类型 - 完整性约束。。。 2. 用户与账户信息 3. 统计描述信息 4. 物理文件组织信息 5. 索引相关信息 SQLDA: 内存数据结构,可以装载关系模式的定义信息!!如列的数目,每一列的名字与类型等 #### ODBC(open database connection) #### ODBC:不同语言的应用程序与不同数据库服务器之间的通讯标准。 ---------- # 数据库系统(中) --- 建模与设计 # 数据模型:表达计算机世界的模型 --- (逻辑层,物理层) 概念模型:编导信息实际的模型 --- (概念层) ![1.png](https://i.loli.net/2021/10/27/uGSYBnl5ta9VEJv.png) 数据建模:将数据建模以便构成模型,关键8字:**理解-区分-命名-表达** #### E-R模型 #### 观点:世界是由实体与其联系构成的。 模型内容: - 实体:实体间可能有联系 --- 如实体的图书与读者。 - 参与发生联系的实体数目称为:元/度 - 可以是一对一,一对多与多对多。 - 属性 - 联系 - 关键字/码:实体中能够用其值唯一区分开实例的属性或属性组合 ### E-R模型 ### #### 1.chen方法 #### - 实体:矩形框 - 属性:椭圆 - 多值属性:双线椭圆 (属性可能有不止一个值,如一个人可以有很多个qq) - 导出属性:虚线椭圆(由其他属性推出来的属性,如由出生年推导出年龄) - 关键字/码:下划线 - 连接:直线 - 联系:菱形框 - 复合关键字:标有相同数字 ![1.png](https://i.loli.net/2021/10/27/dGLi9Ee2aVPQ3MX.png) **联系的表达** ![2.png](https://i.loli.net/2021/10/27/uqQ4K7Pg1B93I6b.png) ![1.png](https://i.loli.net/2021/10/27/KupOHtlsJUmNzy9.png) 示例: ![1.png](https://i.loli.net/2021/10/27/ElQePahVDqFUKG1.png) #### 2.crow's foot方法 #### - 实体:矩形框,名称写横线上 - 属性:实体框横线下面 - 关键字:属性下划线 ![1.png](https://i.loli.net/2021/10/27/HVkvR8aymwSQu26.png) - 联系:菱形框,用线表示联系基数 ![1.png](https://i.loli.net/2021/10/27/GKHVb9Ef3IX4eku.png) ### IDEF1x ### IDEF1x: IDEF标准之一。是E-R图的一个细化 **组成**: 实体: - 独立实体(强实体):实体的实例都被唯一标识,不决定于与其他实体联系。(如合同号) - 从属实体(弱实体):实例的唯一标识依赖于该实体与其他实体的联系(如合同条目依赖合同号区分) --- 从属实体的关键字属性包含了继承的其他实体的属性 联系: - 标定联系:独立实体与从属实体间的联系。父实体的主关键字是子实体主关键字的一部分。 (实线) - 非标定联系:非独立与从属实例的联系,父实体的主关键字不是子实体的主关键字。 (虚线) - 非确定联系:即实体间的多对多联系(需引入相交实体来分解为多个一对多) ![1.png](https://i.loli.net/2021/10/27/hk9iuGYToE7mOI5.png) - 确定联系:一对多/一对一 ### 13.数据库设计 ### ![1.png](https://i.loli.net/2021/10/27/eRIGOvTXasob2yB.png) ## 14. 函数依赖及其定理 ## 函数依赖:在属性集合中,不存在关键码相同但从属属性不同的元组(即,关键码决定从属属性,或,从属属性依赖关键码)。例如,不存在一个学号(关键码)对应不同两个人名(从属属性)。 推广地说,如果某个属性可以对应决定另一个属性(即,该属性的任一值对应另一属性不出现一对多情况),则成该另一属性依赖于此属性。例子见下图。 ![1.png](https://i.loli.net/2021/11/01/PSpFV8ODxZU5QTA.png) ![1.png](https://i.loli.net/2021/11/01/pZoEXcHMrD4Ix9U.png) #### 完全/部分函数依赖 #### 完全函数依赖:若有 X-> Y,且X的任何真子集都不满足与Y的依赖关系,则称Y完全函数依赖于X;反之称部分函数依赖。(很像亲子关系呢) ![1.png](https://i.loli.net/2021/11/01/FU8hw2iGyk9fVTI.png) #### 传递函数依赖 #### 依赖存在传递关系。 ![1.png](https://i.loli.net/2021/11/01/OUtR7akSd9TEoKl.png) #### 相关重要概念 #### 1. 候选键: - 候选键就是满足完全函数依赖的集合,主键就是其中任意一个。 - 超键:完全函数依赖的集合的任意真子集。 2. 外键: - 在当前关系非候选键,但是是另一关系的主键的属性称为外键。外键应满足参照完整性:即外键的值可以为空,但是不能取到对应主键关系所没有的值! 3. 闭包:被F逻辑蕴含的所有函数依赖集合称为F的闭包,记作F+。 #### 阿姆斯特朗公理 #### **属性集的关系模式与函数依赖应满足:** 1. 自反率:一个属性组可以决定它自身的每一个属性。 2. 增广率:一个函数依赖两端各加上相同的属性,函数依赖依旧成立。 3. 传递率:函数蕴含具有传递性。 **引理:** - 合并率:若两个属性组同时依赖于某一个属性集,那么它两的合并也依赖于该集合。(X->Y, X->Z, 则 X->YZ) - 伪传递率:若X->Y, WY->Z, 则XW->Z。 - 分解率:若某属性集对另一集合产生依赖关系,那么这属性集的真子集同样对另一集合满足依赖。(X->Y, Z∈Y,则 X->Z) ### 属性(集)闭包 ### 某属性(集)所能判定的属性(集)就称为该属性(集)的闭包。(也就是存在的函数依赖关系) #### 覆盖 #### 对属性集上的两个函数依赖集合F,G,若F+ = G+, 则称F与G等价,也称F覆盖G(或G覆盖F) **求属性闭包算法** 其实就是利用阿姆斯特朗的传递性等定理,对函数依赖逐个遍历,每次将新的依赖加入原本集合中! ![1.png](https://i.loli.net/2021/11/01/cpwjTxBy2ferbWL.png) **函数依赖集的最小覆盖(最小依赖)** 最小覆盖:等价于一个函数依赖集的最小的函数依赖集。(如上面的依赖集是ABCDE,但实际上这并非是一个完全函数依赖,我们将依赖集中的DE去掉之后,才是一个完全函数依赖;也就是说,最小覆盖或最小依赖)为ABC。 ---------- ## 个人总结14章 ## 依赖就是指属性集合中部分属性是取决于另一部分属性的(如学号可以决定学生姓名等);依赖关系即可以决定其他属性的属性集合(依赖/覆盖)与可以被这些属性所决定的属性的集合(闭包)。 可以发现,依赖实际上就是候选码的集合!主码的选择并无特殊,在依赖里任意选择即可。 并且,依赖分为完全函数依赖与部分函数依赖;完全函数依赖即该依赖集中任一属性都不可再减少的集合(减少了就无法再构成依赖关系);而部分函数依赖即该依赖集的真子集也满足同样的依赖关系(即可以在剔除部分属性同时也保持依赖关系)。 同时,函数依赖也具有传递性等。由阿姆斯特朗公理,依赖满足: - 自反率:属性组可以决定自身的每一个属性! (如AB作为依赖属性组,它一定可以确定A,B这两个属性) - 增广率:依赖关系中两边各加一个属性,依赖关系不变。 (如A->B, 那么一定AC->BC) - 传递率:依赖关系可以传递(如A->B, B->C, 那么一定A->C) 闭包:在函数依赖关系中,依赖集所能决定的所有属性的集合!(求闭包算法,从依赖本身逐个遍历依赖关系并添加) 覆盖:也称依赖。即依赖集。 最小覆盖:也称最小依赖。对某个依赖关系而言,能够保持住该依赖关系的最小依赖组(即刚好满足完全函数依赖时的依赖组)(注,此时依赖的右边即有依赖的属性一定为单个!!!但是左部不一定为单个) ---------- ## 15.三大关系范式 ## ### 1NF ### 属性集中的每一个属性都满足不可分性,则满足第一范式。 - 意义在于,保证每一个属性的非重复性!(如果同时存在两个属性分别为 1. 年月日, 2. 日, 那么第一个属性显然可分且囊括了第二个属性!!!) - 第一范式要求关系中不能有符合属性与多值属性。 ### 2NF ### 若属性集中的每一非主属性都**完全函数依赖**于候选键,则满足第二范式;若不满足,则需要分解函数依赖为多个完全函数依赖!**(该范式消除了非主属性对候选键的部分依赖)** ![1.png](https://i.loli.net/2021/11/01/UcysjIHpQnMKTFu.png) ![1.png](https://i.loli.net/2021/11/01/3wpKotaFCsHrTqN.png) ### 3NF ### 属性集中不应存在传递依赖!若不满足,应分解依赖关系直至没有传递依赖! ![1.png](https://i.loli.net/2021/11/01/PZ6zd8opF2q5m1s.png) ### Boyce-Codd 范式(BCNF) ### 属性集中,不存在不依赖于候选键的函数依赖关系。(和3NF很像哦!但实际上不一样,它要求的是所有依赖关系的依赖集都是候选键!) ### 多值依赖 ### 一个属性可以决定另一个属性(构成依赖关系),且该属性的一个值可以对应依赖关系的属性的复数值。 如,一门课程可以有多个学生在不同时间来上课,则有学生与上课时间多值依赖于课程, 记作: 课程名C ->-> name*time ### 4NF ### 多值依赖的依赖对象一定是超键(候选键或其组合)! #### 总结 #### - 1NF:属性具有原子性(不可再分)---保证属性信息不重复。 - 2NF:不应存在部分函数依赖 --- 消除非主键的部分依赖。 - 3NF:不应存在传递依赖 --- 消除非主键部分的传递依赖。 - BCNF:不应存在不依赖于候选键的依赖关系 --- 保证了所有依赖关系(即覆盖)都是候选键。 - 4NF: 多值依赖的依赖对象必须是超键(候选键或其组合) --- 包含了BCNF,保证了依赖关系都有候选键。 ---------- ## 16.模式分解存在的问题 ## ### 模式分解 ### 将属性集分解成多个子集使其并与其相同。以达到满足上述范式! 分解时需要关注数据本身与其间依赖!!! ---------- #### 无损连接分解 #### 一个大的关系分解成许多个小的关系后,在进行相同查找操作时得到的结果相同的分解称为无损连接分解。 ![1.png](https://i.loli.net/2021/11/01/y95pEasijBLblxv.png) ![2.png](https://i.loli.net/2021/11/01/GwisouePQHN615j.png) ![1.png](https://i.loli.net/2021/11/01/sbavTPqpRZigO85.png) ![2.png](https://i.loli.net/2021/11/01/ueoQdw7CRDryaO1.png) ---------- #### 保持依赖分解 #### 分解后的关系的依赖集合等同于分解总关系的依赖的分解。示例在下: ![1.png](https://i.loli.net/2021/11/01/j8dG3i6PyMoLBnq.png) 检验算法: 对每个关系的依赖进行集合并,查看最后结果是否等于原关系。 ![1.png](https://i.loli.net/2021/11/01/adC4TbBDfyKIjHE.png) #### 模式分解成BCNF与3NF #### 保持依赖且无损连接的算法: ![1.png](https://i.loli.net/2021/11/01/NH457Bg9YFpU8Pf.png) #### 连接依赖与5NF #### 连接依赖:差不多等于无损连接 5NF: 关系的每个连接依赖都按其候选键进行连接运算即满足第五范式。 ---------- ## 14~16章总结 ## ![1.png](https://i.loli.net/2021/11/01/zmu4Q3RDiHAlpwT.png) ---------- # 数据库下:管理与技术 # ## 17. 数据库物理存储 ## ### 1. 计算机的存储体系 ### ![1.png](https://i.loli.net/2021/11/02/1QeJkM9FucWhKE4.png) #### DBMS的数据存储与查询实现的基本思想 #### ![1.png](https://i.loli.net/2021/11/02/zEiOjXtNhDYv79l.png) ### 四种文件组织方法 ### #### 1.堆文件 #### 特点:无序(更新效率高,检索效率低) ![1.png](https://i.loli.net/2021/11/02/l7XyuDfJbpoTevz.png) ![2.png](https://i.loli.net/2021/11/02/nT8LewifCxFoO4p.png) #### 2. 有序记录文件 #### 特点:根据属性顺序插入,存储记录有序,检索快,更新慢。 ![1.png](https://i.loli.net/2021/11/02/CO4DURgTEdJ1PL2.png) ![2.png](https://i.loli.net/2021/11/02/USdHxKyNXMwFcZT.png) ![3.png](https://i.loli.net/2021/11/02/jYoHFNfrWQd9ywR.png) #### 3. 散列文件 #### 利用哈希表与链表的结合,在查询与更新均有一定程度的优化。 ![1.png](https://i.loli.net/2021/11/02/vKsbXrqlih6yju1.png) ![2.png](https://i.loli.net/2021/11/02/uQCiLHSWcljsD4p.png) ![3.png](https://i.loli.net/2021/11/02/zbMerq58J2GxVSh.png) #### 4. 聚簇文件 #### 将有相同或类似属性值的记录存放于连续的磁盘簇块。 多表聚簇:将若干个关联的Table存储在一个文件中可提高多表查询速度。 ### openGauss存储技术 ### ![1.png](https://i.loli.net/2021/11/02/toTq4QGkBpfbize.png) ![2.png](https://i.loli.net/2021/11/02/xY5MgR7dp19GUZN.png) ## 18. 数据库索引 ## **索引文件**:快速定位所需记录的辅助存储结构,存在与否不影响存储表的物理结构,存在的话可以提高存储表的访问速度; - 排序索引文件:按索引字段值的顺序组织存储 - 散列索引文件:按哈希存储 ![1.png](https://i.loli.net/2021/11/02/WUSlJirXhpA3RbL.png) ![2.png](https://i.loli.net/2021/11/02/86H9dDQc2ky3MnL.png) ![1.png](https://i.loli.net/2021/11/02/jGAFzuTRwcDLtIX.png) #### SQL索引创建与维护 #### 定义table后,系统会根据主键自动创建主索引! 创建索引: CREATE [unique] INDEX 索引名 ON 表名(列名) eg. CREATE INDEX index1 ON student(sname) 撤销: DROP INDEX index1 ### 稠密索引与稀疏索引 ### 稠密索引:对于主文件的每一个记录形成的索引字段值,都有一个索引与其对应的称为稠密索引。 相反,对于主文件部分记录有索引项与其对应的索引称为非稠密索引或稀疏索引。稀疏索引的空间占用少,维护任务轻,但速度慢。 **问题**:如何利用稀疏索引进行查询? 方法:定义索引字段值为k的记录,先利用稀疏索引查找相邻的小于k的最大索引字段对应的索引项,再开始往下顺序检索Table。 #### 主索引与辅助索引 #### 主索引:对每个存储块都有一个索引项,索引项的总数与存储表所占的存储块数目相同。存储块的第一条记录称为锚记录。**主索引是稀疏索引** 辅助索引:**稠密索引**,排序字段的每一个不同值都有索引项。 一个主文件:有一个主索引与复数个辅助索引。主索引建立于主码/排序码上;辅助索引建立于其他属性上。 #### 聚簇索引 #### 在索引文件中的值,在主文件中也是相邻(聚簇)存储的。 一个主文件只能有一个聚簇索引文件。且主索引通常是聚簇索引!!! ### B+ Tree 索引 ### **多级索引**:对索引再建索引的辅助数据结构 **B+树索引:**以树型数据结构来组织索引项的多级索引。每一个结点都是一个存储块。叶子节点的指针指向主文件数据块或对应记录。 ![1.png](https://i.loli.net/2021/11/02/36IGFmN9zf5OA18.png) ![2.png](https://i.loli.net/2021/11/02/cI6irXK8Gx5PhuO.png) **B树索引** - 索引字段值只出现一次,可以是叶节点或非叶节点 - 指向主文件的指针出现于叶节点或非叶节点(B+树只出现在叶节点) ### B树与B+树比较 ### 不同: 1. 指针: - B树的叶节点与非叶节点的指针都指向主文件,而B+树仅有叶节点指针指向主文件; 2. 字段值: - B树内所有索引字段值只出现一次,而B+树会出现多次; **因此B+树深度更小,IO操作次数更少。** #### 散列索引 #### 利用散列表解决问题。 麻烦:怎么确定散列长度?选择哈希函数后保证80左右利用率情况的话,新增或删除操作进行后,怎么办? 选择:使用动态散列! 1. 可扩展散列索引:利用二进码的后k位不同,使用长度为2**k 的散列表。 2. 线性散列索引:保持80%利用率,不够就加一长度。 问题:长度改变后,哈希函数是否改变?之前部分的映射是否全部撤销?----未解之谜 ## 19. 数据库查询实现算法-一趟扫描 ## 首先,连接运算的物理层次(读入内存)的算法实现主要以下四种,分别考虑两个关系与内存块的大小进行选择。第一个算法是最老实的四次for循环,过于复杂;当内存可以一次性装入某一个关系时,为了减小复杂度,最好将关系直接装入!那就会有下面的几种算法。 最后一种,也就是两个都无法全部装入内存。这时候我们就一次性把内存分开,假设内存有M块,我们用M-2块装第一个关系的一部分,剩下两块分别装第二个关系的小部分与输出!这样我们对于每次读入的多的那个关系而言,读入次数就变少了! ![1.png](https://i.loli.net/2021/11/04/euX5ISbCx7fzYLj.png) #### 迭代器算法 #### ![1.png](https://i.loli.net/2021/11/04/zqVB1yPFHKshtZd.png) ---------- #### 数据库查询的一趟扫描方法 #### ![1.png](https://i.loli.net/2021/11/04/Wt8mFygUSnb5HqL.png) **实现去重复查询操作** 也就是: SELECT DISTINCT * FROM table 利用散列或者树等结构储存已有记录对比去重 ![1.png](https:/i.loli.net/2021/11/04/9FnsRVvDJ4xwUIY.png) ---------- #### 分组聚集 #### 也就是: GROUP BY 。。。 想法:建立结构分组完成后再输出(各种结构都可以) ![1.png](https://i.loli.net/2021/11/04/uDLlYTIW9Zd2pjO.png) ---------- ## 20. 数据库查询实现算法-两趟扫描 ## 为什么需要两趟扫描?---如果两个关系都比内存块大,那么一趟扫描是不够的,需要两趟甚至多趟扫描。 ![1.png](https://i.loli.net/2021/11/04/niM4tGohwB36E2a.png) ![1.png](https://i.loli.net/2021/11/05/Vjcx316gO7qCfkS.png) ![1.png](https://i.loli.net/2021/11/05/jCQm1BVISO84wFa.png) ### 两阶段多路归并排序TPMMS ### **内排序问题**:待排序数据可一次性装载入内存,排序者可完整看到以及操纵所有数据。 **外排序问题**:待排序数据无法一次装入内存的问题。 **TPMMS**:两阶段多路归并排序处理外排序问题。 - 一阶段:将每次读入的子集合进行排序; - 二阶段:对于多个有序的子集合,利用内存进行总排序。 **问题:如何解决!!??** ![1.png](https://i.loli.net/2021/11/05/HpLwzXYcQZETisa.png) ![1.png](https://i.loli.net/2021/11/05/YvyWpVJMrhRKPoa.png) ### 基于排序的两趟扫描算法 ### #### 分组(GROUP),去重(DISTINCT),集合的操作(并交差) #### 1. 第一趟:划分子表,排序; 2. 第二趟:归并:最大取M-1内存块,进行上述操作(一块输出); ---------- ### 基于散列的两趟扫描算法 ### ![1.png](https://i.loli.net/2021/11/05/bToY1zOCJGVpuZr.png) #### 分组(GROUP),去重(DISTINCT),集合的操作(并交差) #### 1. 第一趟:利用散列函数将原始关系散列成子表; 2. 第二趟:最大取M-1内存块,进行上述操作(一块输出); - 去重:元组在子表上不重复,则在大关系不重复(因为散列时候已经将可能重复的散列到一起!) - 分组:同一分组的所有记录应在同一子表中! - 并交叉:同上,在散列时已经有一定的分类效果。 ## 21. 数据库查询优化技术 ## 查询优化:使数据库查询时间最短! --- 在合并等操作前前先筛选,减少每次参加关系运算的集合大小。 **总体思路**: - 逻辑优化:寻找操作次数最少的关系运算 - 尽可能早地进行选择和投影; - 把选择与投影串接; - 把投影与其前后二维运算结合; - ... ![1.png](https://i.loli.net/2021/11/05/eoBlV7HpKxqdQsG.png) - 物理优化:利用物理查询运算符(各种表空间排序算法,如之前的一趟,两趟等...)快速搜索到块区。 总览: ![1.png](https://i.loli.net/2021/11/05/GFLhfWaixnVcdew.png) ## 22. 事务处理之并发控制 ## 为什么需要并发控制? - 假设许多个人同时在云端购买车票,如果没有并发控制,他们有可能买到一样的车票!!! - 并发控制保证了事务的一致性。 - 不然会发生三种典型的不一致现象: - 丢失修改:两个事务同时修改某一数据容易丢失修改 - 不能重复读 - 脏读:在一个事务读取数据时,若另一个事务对其进行修改,那么它会读取到修改后的值;但如果在之后卷回另一事务(也就是撤销修改),第一个事务还是读到修改后的值,也就是读到无效值。 **并发控制(DBMS核心技术!!!)**: 1. 基于封锁法 2. 基于撤回法 ---------- ### 事务 ### **事务**: DBMS管理并操纵数据库的一种手段。 - 宏观:一个存取或改变数据库内容的程序的一次执行。需要提交或卷回。 - 微观:对数据库的一系列基本操作的一个整体性执行。 - 并发执行:宏观上来看事务是独立执行的,但是围观看来事务可以是交错执行的! ---**因此我们需要并发控制!** **事务的特性ACID**: - A: 原子性:事务原子不可分,要么全做,要么全不做; - C: 一致性:事务应符合一致性操作规则,不出现上面的三种不一致性; - I: 隔离性:多个事务间互不影响; - D: 持久性:已提交事务的影响是持久的;卷回事务的影响是可恢复的。 ## 也就是说,具有ACID特性的若干数据库操作的组合就是事务! ## ### 事务调度与可串行性 ### **事务调度**:一组事务的基本步(读,写,其他控制操作如加锁,解锁等)的执行顺序称为对事务的调度。 **并发调度**:多个事务从宏观看来是并行执行的,但微观上是交叉执行的!容易带来不一致性! **可串行性**:若一个调度对数据库状态的影响都和某个串行调度相同,则称这个调度有可串行性。 **冲突**:调度中一对连续的动作,满足:如果顺序交换,则涉及的事务中至少有一个事务的行为会改变。有冲突的两个操作是不能交换次序的!!!! 常见冲突: - 同一事务的任何两个操作都冲突; - eg. ri(X); wi(Y) - 都属于第i个事务,不可交换读写顺序 - 不同事务对同一元素的两个写操作冲突; - eg. wi(X); wj(X) - 不同事务,但都是写入X,交换会出错 - 不同事务对同一元素的一读一写也冲突; - eg. wi(X); rj(X) - 不同事务,交换读写顺序会改变读的值,不可交换 **冲突可串行性**:若某个调度可以通过交换相邻变为串行且无冲突,则具有冲突可串行性!!! - 满足冲突可串行性则一定满足可串行性! ---------- ### 基于封锁的并发控制! ### 锁:控制并发的手段,读写数据前获得锁,处理完成后释放锁!被锁的元素在该事务完成前不可被其他事务使用!**运用锁可以保证冲突可串行性,但是锁本身不可以保证冲突可串行性!!!!** **锁的类型**: - 排他锁(X):只有一个事务可以读,写!(该事务加锁后,其他所有事物无法再对这个元素申请任何锁) - 共享锁(S):所有事务都可以读,但任何事物都不能写!(该事务加锁后,其他事务也可申请S锁,但不能申请X锁!) - 更新锁:初始读,以后可以升级为写! - 增量锁:可增量更新 #### 两段封锁协议 #### **两段封锁协议2PL**:读写数据前要获得锁。每个事务中所有封锁请求都要先于任何的解锁请求。 也就是说,两段:加锁段,解锁段。加锁段中不能解锁,解锁段中不能加锁。 **两段封锁协议可以保证冲突可串行性!!** 但是,**两段封锁协议可能产生死锁**!!就是谁也不让,都在等待对方解锁! ---------- ### 基于时间戳的并发控制! ### 对每个步骤赋予时间戳,并按时间戳大小顺序执行事务(就像教室上课先后顺序),这样就可以不用锁进行并发控制。 ---------- ### 基于有效性确认的并发控制 ### 通过对每个事务的读写数据储存时间戳集合,利用多个读写集合判断是否有冲突的方法称为有效性确认! ## 23. 故障恢复 ## #### 数据库故障类型及影响 #### - 事务故障:某一个事务自身运行错误引起的故障,只影响事务本身 - 系统故障:由于非正常关机等引起的故障,影响很大 - 介质故障:由于磁盘等介质损坏引起的故障,影响全面,又影响内存中数据,也影响介质上数据。 ### 故障恢复 ### 目的:保证事务的原子性与持久性。 #### 事务故障恢复 #### 取消/滚回事务 #### 系统故障恢复 #### 通过运行日志恢复。重做故障前检查点后启动,故障点前完成的所有事务,撤销故障点时未完成的所有事务!!! **运行日志**:DBMS的维护文件,以流水方式记录数据库的每次操作及顺序。 #### 介质故障恢复 #### 采用副本替换损坏数据库!(也就是copy)以恢复介质内容;再使用运行日志恢复事务等内容。 ### 日志 ### **日志**:包含日志记录的只能追加的顺序文件,不同是无的日志记录交错存储,按发生时间存储。(应该就是运行日志) 发生系统故障时,使用日志恢复: - 故障时已提交事务:重做 - 故障时未提交事务:撤回 分为: 1. UNDO日志 2. REDO日志 3. UNDO/REDO日志 #### UNDO日志 #### 对于事务T,UNDO日志按先后顺序记录: 1. 首先写入运行T更新**前**的值; 2. OUTPUT(X),写磁盘; 3. 记录T是被运行(COMMIT)或卷回(ROLLBACK); - **恢复**:从**后往前**看,撤销所有未完成事务的所有修改 #### REDO日志 #### 对于事务T,REDO日志按先后顺序记录: 1. 首先写入运行T更新**后**的值; 2. 若提交了事务则写入 COMMIT T; 3. OUTPUT(X),写磁盘; - **恢复**:从**前往后**看,仅仅COMMIT 表示事务完成了,进行REDO ---------- #### UNDO/REDO日志 #### UNDO日志: - 先OUTPUT - 若无COMMIT,则全部UNDO(从后往前看)--可能影响性能(因为可能频繁写磁盘) REDO日志: - 后OUTPUT - 若COMMIT不可见则无需撤销,可见则REDO---灵活性差(因为数据要再COMMIT后才可见) **UNDO/REDO日志:灵活!!! ** 对于事务T,REDO日志按先后顺序记录: 1. 首先写入运行T更新**前后两方**的值; 2. 若提交了事务则写入 COMMIT T或OUTPUT(X),写磁盘(无需考虑先后) - **恢复**:从**前往后**看,仅仅COMMIT 表示事务完成了,进行REDO; 从**后往前**看,撤销所有未完成事务的修改,也就是进行UNDO