1-绪论
1.1-概念
- 数据库系统包含:数据库,数据库管理系统,数据库管理员,应用系统
- 数据库:数据库是长期存储在计算机内,有组织的,可共享的大量数据的集合,数据库中的数据按照一定的数据模型,组织,描述和储存,具有较小的冗余度,较高的数据独立性和易扩展性,并可为各种用户共享
- 数据库数据特点
- 永久存储
- 有组织
- 可共享
- 数据库管理系统:负责科学地组织和存储数据,高效的获取和维护数据
- 功能:
- 数据定义功能(DDL)
- 数据操纵功能(DML和DQL)
- 数据组织,存储和管理
- 数据库的事务管理和运行管理:保证数据安全性,完整性。
- 数据库的建立和维护功能
- 数据管理技术的发展
- 人工管理阶段
- 特点
- 数据不保存,无直接存储设备
- 应用程序管理数据
- 数据不共享
- 数据不具有独立性
- 无操作系统
- 数据处理方式为批处理
- 数据是无结构的
- 缺点:数据的逻辑结构或物理结构发生变化后,必须对应用程序就相应的修改,这就加重了程序员的负担
- 文件系统
- 特点:
- 数据可以长期保存(磁盘,磁鼓)
- 有文件系统管理数据
- 数据处理方式为联机实时处理和批处理
- 记录内有结构,整体无结构
- 缺点:
- 数据的共享性差,冗余度大
- 数据独立性差
- 数据库系统
- 特点:
- 数据结构化
- 数据的共享性高,冗余度低,易扩充
- 数据独立性高
- 数据由DBMS统一管理和控制
- 处理方式为联机实时处理,分布处理,批处理
- 具有高度的物理独立性和一定的逻辑独立性
- 数据整体结构化,用数据模型描述
- ==由文件系统到数据库系统标志着数据管理技术的飞跃
1.2-数据模型
- 概念:数据模型(Data Model)也是一种模型,他是对现实世界数据特征的抽象。数据模型就是现实世界的模拟
- 两类数据模型
- 概念模型(信息模型):按照用户的观点来对数据和信息建模,主要用于数据库设计。
- 逻辑模型和物理模型
- 逻辑模型主要包括:
- 层次模型
- 网状模型
- 关系模型---->二维表
- 面向对象模型
- 对象关系模型
他是按计算机系统的观点对数据建模,主要用于DBMS的实现
- 物理模型是对数据最低层的抽象,它描述数据在系统内部的表示方式和存取方式,在磁盘或者磁带上的存储方式和存储方法,是面向计算机系统的
- 数据模型的组成要素
- 由三部分组成
- 数据结构:数据结构描述数据库的组成对象以及对象之间的练习,数据结构是所描述的对象类型的集合,是对系统静态特性的描述
- 数据操作:查询和更新(增删改),是对系统动态特性的描述
- 完整性约束:是一组完整性规则
- 例如:在关系模型中,任何关系必须满足实体完整性和参照完整性两个条件
- 实体完整性:每一个数据,都应该设立一个关键字,可以唯一确定一条记录,比如说身份证号
- 参照完整性:
- 用户自定义完整性
1.2.1-概念模型
- 概念:概念模型是现实世界到机器世界的一个中间层次,表现为:
- 概念模型用于信息世界的建模
- 现实世界到信息世界的第一层抽象
- 数据库设计人员进行数据库设计的有力工具
- 数据库设计人员和用户之间进行交流的语言
- 要求:
- 具有较强的语义表达能力
- 可以方便。直接的表达应用中的各种语义知识
- 简单清晰,易于用户理解
- 信息世界中的基本概念
- 实体:客观存在并且可以相互区分的事物,比如学生,教师,部门,课都是实体
- 属性:实体具有的特征,例如学生的学号姓名年龄等等
- 码:唯一标识实体的属性或属性集,例如学号是学生的码
- 域:属性的取值范围,比如政治成绩的范围为0-100
- 实体型:具有相同属性的实体肯定有共同的特征和性质,比如学生(学号,性别,姓名,出生日期)就是一个实体型
- 实体集:全体学生就是实体集
- 联系:实体内部的联系(组成实体的属性间的关系),和实体间的联系(不同实体集的联系)
- 一对一联系:
- 一对多联系:
- 多对多联系:
- 注意:不要忽略实体内部的联系,比如职工中有领导与被领导的关系,所以ER图可以画为
- 概念模型的一种表示方法:ER图
- 实体型:矩形
- 属性:椭圆形
- 用无向边连接
- 联系用菱形
- 联系也可以有属性
1.2.2-关系模型
- 常见的模型:
- 层次模型:树型
- 网状模型:工程图
- 关系模型
- 关系模型的数据结构
- 关系:一个关系通常对应一个表
- 元组:表中一行即为一个元组
- 属性:表中的一列为一个属性,与属性值要区分开
- 码:这个属性集可以唯一确定一个元组
- 域:属性的取值范围
- 分量:元组中的一个属性值
- 关系模式:对关系的描述,一般表示为:关系名(属性1,属性2,…,属性n)
- 优点:
- 与格式化模型不同,是建立在严格的数学概念上的
- 关系模型概念单一
- 关系模型的存储路径对用户透明,有更高的数据独立性,更好的安全保密性,简化了程序员的工作,和数据开发建立的工作 :三级模式,两级映射
1.3-数据库系统结构
1.3.1-三级模式两级映射
- 模式: 数据模型中有“型”和“值”的概念,如:学生(学号,性别,班级)是“型”,而:张三(20160310001,1,3年2班)是“值”。 模式(Schema)是数据库中全体数据的逻辑结构和特征的描述,它仅仅描述型,而并不包括值。模式的值称为模式的一个实例(Instance),同一个模式可以有多个实例。
- 三级模式:
- 外模式(用户模式):
- 它是数据库用户(应用程序员、最终用户)能够看到的使用的局部数据的逻辑结构和特征的描述,是数据库的数据视图,是与某一个应用有关的数据的逻辑表示。
- 外模式通常是模式的子集。一个数据库可以有多个外模式。同一个外模式可以为某一用户的多个应用系统所使用,但一个应用系统只能使用一个外模式。
- 外模式是保证数据库安全性的一个有力措施。每个用户只能看见和访问所对应的外模式中的数据,数据库中的其余数据是不可见的。
- DBMS提供子模式描述语言(子模式DDL)来严格地定义子模式
- 模式(逻辑模式/概念模式):
- 它是DB中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。
- 模式层是数据库模式结构的中间层,既不涉及到数据的物理存储细节和硬件环境,也与具体的应用程序、应用开发工具以及高级程序设计语言无关(C、C++、JAVA等)。
- 模式就是数据库数据在逻辑上的视图,且一个数据库只有一个模式。实际工作中,模式就等同于程序员创建一个具体的数据库的全部操作,如:这是一个MySQL数据库,有2张表,每个表的名字,属性的名字、类型、取值范围,主键,外键,索引,其他完整性约束等等。
- DBMS提供模式描述语言(模式DDL)来严格地定义模式。
- 内模式(存储模式): 一个数据库只有一个内模式。它是数据库物理结构和存储方式的描述,是数据在数据库内部的表示方式。如:记录的存储方式是堆存储,还是按照某些属性值的升(降)存储,还是按照属性值聚簇(cluster)存储;索引按照什么方式组织,是B+树索引,还是hash索引等等。
- 两级映射: 这两层映像保证了数据库中的数据能够具有较高的逻辑独立性和物理独立性。
- 外模式/模式映像:外模式/模式映像保证了当模式改变时,外模式不用变 — 逻辑独立性。
- 由上可知:一个DB只有一个模式,但可以有多个外模式。 所以,对于每一个外模式,数据库系统都有多个外模式/模式映像,它定义了这个外模式与模式的对应关系。外模式的描述中通常包含了这些映像的定义。
- 当模式改变时(增加新的关系、新的属性、改变属性的数据类型等),由数据库管理员对各个外模式/模式映像作相应的改变,可以使得外模式保持不变。而又由于应用程序应该是依据外模式编写的,从而应用程序不必修改,这就保证了数据与程序的逻辑独立性。
- 模式/内模式映像:模式/内模式映像保证了当内模式改变时,模式不用变 — 物理独立性。
- 由上可知:一个DB只有一个模式,也只有一个内模式,所有模式/内模式映像是唯一的,它定义了数据全局逻辑结构与存储结构之间的对应关系。
- 当数据库的存储结构改变时(例如选用了另一个存储结构),由数据库管理员对模式/内模式映像作出相应的改变,可以使得模式保持不变,从而应用程序也不必改变。这就保证了数据和程序的物理独立性。
1.4 数据库系统的组成
- 数据库系统一般由:数据库,数据库管理系统,应用系统和数据库管理员组成
- 组成:
- 硬件
- 软件:
- DBMS
- 支持DBMS的操作系统
- 高级语言编辑系统
- 应用开发工具
- 数据库应用系统
- 人员:
- 数据库管理员:DBA,完整性约束条件
- 系统分析员:ER图之类的,系统需求分析和规范说明,要与用户沟通
- 设计人员
- 应用程序员
- 最终用户
2-关系数据库
2.1-关系数据结构及形式化定义
2.1.1-关系
- 概念:从用户角度来说,关系模型中数据的逻辑结构是一张二维表,关系模型是建立在集合代数的基础上
- 相关定义:
- 域:具有相同数据类型的值的集合
- 笛卡儿积:给定一组域D1 D2…Dn这n个取值范围(域),所有域的所有取值的一个组合,会产生在现实生活中无实际意义的数据
- 笛卡儿积的表示方法:二维表,每行对应一个元组,每一列对应一个域
- 元组:(20171611607,何佳乐,男)就是一个元组,其实就是关系中的一行
- 分量:笛卡儿积元素(d1,d2…dn)中的每一个值di称作分量,例如20171611607,何佳乐,男是三个分量 ----其实就是一个单元格
- 基数:针对域来说的,域有几个取值,基数就是几,例如性别{男,女}的基数就是2,域相乘的基数就是各个域取值范围相乘
- 关系:
- 表示方式R(D1,D2,D3,…,Dn) R表示关系名,n可以称为关系的度或目
- 元组:一般用t表示
- 单元关系 n=1 二元关系 n=2
- 关系的表示方式:二维表,行为元组,列为属性
- 属性
- 码
- 候选码:可以唯一标识一个元组的属性或者属性集,可以有多个候选码,比如学号可以确定一个学生,身份证也可以,所以该表就有两个候选啊没
- 主码:上面有两个候选码,人为的选择一个作为主码就可以
- 全码:极端情况下,数据库中所有的码组成一个候选码,就称为全码
- 主属性:属于候选码中的属性就是主属性
- 非主属性:不属于候选码中的属性就是非主属性
- 三类关系
- 基本关系(基本表或基表):是数据库中实实在在存在的表,是实际存储数据的逻辑表示
- 查询表:查询结果对应的表
- 视图表:由基本表或其他视图表导出的表,是虚表,不对应实际存储的数据
- 基本关系的性质
- 同一列的数据具有相同的性质,列是同质的
- 不同的列可以出自同一个域,不同列要起不同的属性名
- 列的顺序无所谓,可以任意交换
- 任意两个元组的候选码不可以相同
- 行的顺序无所谓,可以任意交换
- 分量必须取原子值,不可以表中有表(拆分单元格),涉及到范式
2.1.2-关系模式
- 概念:关系模式是对关系的描述,关系模式是型,关系是值
- 元组集合的结构:属性构成,属性来自的域,属性与域的映射
- 关系要遵循一定的完整性约束
- 关系模式表示方式:R(U,D,DOM,F) 区别于关系的表示方式
- R:关系名
- U:组成该关系的属性名的集合
- D:属性集中属性所来自的域
- DOM:属性和域的映射关系
- F:属性间的数据依赖关系集合
- 关系模式和关系:
- 关系模式是静态的,稳定的
- 关系是动态的,随时间不断变化的
- 关系是关系模式在某一时刻的状态或内容
2.2-关系数据结构
2.2.1-基本关系操作
- 常用的关系操作:
- 查询:选择,投影,连接,除,并,交,差,其中:选择,投影,并,差,笛卡儿积是五种基本操作
- 数据更新:插入,删除,修改
- 特点: 是集合操作方式,即操作的对象和结果都是集合
2.2.2-关系数据库语言的分类
- 概念:
- 关系代数语言
- 关系演算语言
2.3-关系完整性
- 概念:
- 实体完整性:若属性A是基本关系R的主属性,则属性A不可以取空值(空值就是不知道,不存在,或无意义的值)
- 实体完整性是针对基本关系而言的,一个基本表通常对应现实世界的一个实体集
- 现实世界中的实体是可区分的,即他们具有唯一性的标识
- 关系模型以主码作为唯一性标识
- 主码中的属性不可以取空值,主属性取空值,就说明存在不可标识的实体,矛盾
- 如选修表(学号,课程号,成绩),(学号,课程号)为主码,则两个属性都不可以取空值
- 参照完整性
- 关系间的引用:在关系模型中实体以及实体间的联系都是用关系来描述的,所以会存在关系间的引用
- 例如:学生表(学号,姓名,性别,专业号,年龄) 专业(专业号,专业名)
- 学生关系引用了专业关系的主码:专业号,所以,学生关系中的专业号的值必须是在专业表中存在的,即专业表中有该记录
- 外码:设F是基本关系R的一个或一组属性,但不是关系R的码,如果F与基本关系S的主码Ks相对应,则称F是基本关系R的外码
- 基本关系R称为参照关系
- 基本关系S称为被参照关系或目标关系
- 关系R和关系S不一定是不同的关系,比如学生中挑一个班长(关系内部的联系),这就是同一个表中
- 外码不一定要与相应主码同名
- 规则:
- 若属性或属性组F是基本关系R的外码,他与基本关系S(外码做主码的表)相对应,则对于R中每个元组在F上的值必须为:
- 或者取空值(F的每个属性值都为空值)
- 或者等于S中某个元组的主码值
- 例如学生关系(学号,姓名,性别,专业号,年龄),专业号就要么为空(表示该学生未分配专业),要么为非空值,此时该值必须是专业表中存在的值(表示该学生不可能分配到一个不存在的专业)
- 用户自定义完整性:是针对某一具体的关系数据库的约束条件,反应某一具体应用所涉及的数据必须满足的语义要求
- 例如:课程(课程号,课程名,学分),课程号要求必须唯一值,非主属性课程名要求不能取空值,学分属性的取值范围为:1,2,3,4,以上就是用户自定义完整性约束
- 其中:实体完整性和参照完整性称为关系的两个不变性
2.4-关系代数
- 概念:是一种抽象的查询语言,关系代数的运算对象是关系,运算结果也是关系,关系代数按运算符的不同,可以分为集合运算和专门的关系运算两类
- 集合运算是从关系的水平方向,即列的角度进行
- 专门的关系运算不仅涉及行而且涉及列
- 分类:
- 集合运算符
- 关系运算符
2.4.1-传统的集合运算符
- 并:关系R和S具有相同的目n(即两个关系都有n个属性),相应的属性取自同一个域
- 运算结果:还是n目关系,由属于R或属于S的元组组成
- 简而言之:把两个表包含的全部元组放一起,去掉重复的
- 交:关系R和S具有相同的目n(即两个关系都有n个属性),相应的属性取自同一个域
- 运算结果:还是n目关系
- 简而言之:把两个表都有的元组筛选出来
- 差:关系R和S具有相同的目n(即两个关系都有n个属性),相应的属性取自同一个域
- 运算结果:还是n目关系
- 简而言之 A-B就是把A有的,B没有的元组筛选出来
- 笛卡儿积:目可以不同,可以是不同的属性
- 运算结果:列:两者属性个数和 行:两表元组个数相乘
- 简而言之:把两个表的元组排列组合的所有结果列出来
2.4.2-专门的关系运算
- 关系运算包括:选择,投影,连接,除运算
- 相关计号说明:
- t:代表一个元组,例如(20171611607,何佳乐,男)
- t[Ai]:代表元组上对应Ai属性的一个分量 t[姓名] 就是何佳乐
- 象集:很抽象,象集由大写Z表示
姓名 | 成绩 | 性别 |
何佳乐 | 100 | 男 |
李四 | 90 | 男 |
王五 | 90 | 男 |
例如:性别男,在关系上的象集为:{(张三,100),(李四,90),(王五,90)}
成绩90,在关系上的象集为:{(李四,男),(王五,男)}
成绩90,性别男,在关系上的象集为:{李四,王五}
- 选择: (从行的角度进行的运算) , 选择运算是根据某些条件挑选出元组(即行)
- σF® = {t|t∈Rt∈R∧F(t)= ‘真’} F是一个选择条件,即从R中选出满足F为真的行。
- 投影
- 连接(连接运算为=的称为等值连接):
- 等值连接
- 自然连接,是一种特殊的等值连接(就是等值连接中去掉两表相同的属性列,符号一样,但是符号下面不写任何比较符号)
- 悬浮元组:两个表进行自然连接的时候舍弃的元组就是悬浮元组
- 外连接:如果把悬浮元组舍弃的元组也保存在结果里面,就是外连接
- 左外连接 保留左表的悬浮元组
- 右外连接 保留右表的悬浮元组
3-SQL
3.1-SQL概述
- 概念:结构化查询语言,是关系数据库的标准语言,SQL是一个通用的,功能极强的关系数据库语言
- 特点:
- 综合统一
- 高度的非过程化
- 无需制定存储路径
- SQL只需提出做什么,而无需了解存储路径
- 存储路径的选择以及SQL的操作由系统自动完成
- 面向集合的操作方式
- 非关系型数据库是面向记录的操作方式
- SQL采用集合操作方式
- 操作对象,查找结果可以是元组的集合
- 一次插入,删除,更新操作的对象
- 可以是元组的集合
- 以同一种语法结构提供多种使用方式
- 可以独立的用于联机交互
- 又可以作为嵌入式语言,嵌入到高级语言里面
- 简单
- 数据查询:select
- 数据定义:create drop alter
- 数据操纵:insert update delete
- 数据控制:grant,revoke
- 基本概念:SQL支持关系数据库三级模式结构
- 视图—外模式
- 从一个或多个基本表导出的表
- 视图是一个虚表
- 用户可以在视图上继续定义视图
- 数据库中只存放视图的定义而不存放视图对应的数据
- 基本表—模式
- 本身独立存在的表
- SQL中一个关系对应一个表
- 一个或多个基本表对应一个存储文件
- 一个表可以带若干个索引
- 存储文件—内模式
- 逻辑结构组成了关系数据库的内模式
- 物理结构是任意的,对用户透明
3.2-数据定义
3.2.1-模式的定义与删除
- 模式定义:create schema 模式名 authorization 用户名
- 若没有制定模式名,则默认为用户名 create schema authorization 用户名
- 执行创建模式语句必须要有DBA权限,或者被DBA授予create shema权限
- 模式删除:drop shema 模式名 <cascade/restrict>
- cascade和restrict二选一
- cascade:级联,删除模式的同时删除该模式中的所有数据库对象全部删除
- restrict:限制,若该模式中定义了下属的数据库数据库对象(表,视图),则拒绝执行该删除操作,只有当该模式中没有任何下属的对象的时候才可以执行
- 例如 drop scheam zhang cascade 删除模式zhang,同时删除其下属的表tab1
3.2.2-基本表的定义,删除与修改
- 定义基本表:如果完整性约束条件涉及到该表的多个属性列,则必须定义在表级上,否则既可以定义在列级上也可以定义在表级上
- 列级完整性约束条件:
- sno CHAR(6) PRIMARY KEY 定义主键
- foreign key(cpno) references course(cno)
- 表级完整性约束条件
- primary key(sno,cno) 主码由两个属性构成,必须作为表级完整性进行定义
- varchar和char的区别
- 都是字符串,但是varchar(x)是最长为x的可变长字符串
- char(x)是定长为x的字符串
- numeric(p,d):定点数,总长p位,小数点后d位
- 模式与表:
- 每一个基本表都属于某一个模式,一个模式可以由多个基本表,创建基本表的时候,若没有指定模式,系统会根据搜索路径来确定该基本表所属的模式
- 显示当前的搜索路径 show search_path;
- 搜索路径的当前默认值:$user ,PUBLIC;
- DBA用户可以设置搜索路径 set search_path to ‘s-t’,public 😭)
- 创建基本表
- 创建表的时候给出模式名
- 例如 create table “wang”.student(…),在模式wang下创建表student
- 在创建模式语句中同时创建表
- create shema ”s-t“ authorization wang
- create table tab1(
- sno char(9) primary key
- );
- 设置所属模式,在创建表名中不必给出模式名–直接写创建表的语句就可以
- 修改基本表
- 增加列:alter table tableName add columnName varchar(30)
- 修改列类型:alter table tableName alter column columnName varchar(4000)
- 修改列名称: ALTER TABLE [表名.]TABLE_NAME RENAME COLUMN OLD_COLUMN_NAME TO NEW_COLUMN_NAME
- 删除列:alter table tableName drop column columnName
- 增加约束条件:alter table tableName add 约束条件(列名)
- 删除基本表:drop table tableName cascade/restrict
- restrict:删除表是有限制的,欲删除的的基本表不可以被其他的表引用,必要要删除选课表,就要先删除sno和cno对于的被参照关系学生表和课程表。再比如,若表上建有视图,那么也不可以删除。
- cascade:删除该表没有限制,在删除该表的同时,会删除该表的依赖对象
- 基本表的定义被删除,数据被删除,表上建立的索引,触发器等一般也将删除
3.2.3-索引的建立与删除
- 建立索引的目的:加快查询速度
- 谁可以建立索引:DBA或表的属主
- DBMS一般会再以下列上自动建立索引:
- primary key
- unique
- 谁维护索引:DBMS自动完成
- 使用索引:DBMS自动选择是否使用索引
- 关系数据库关系系统中,一般采用B+树,Hash索引来实现
- B+树具有动态平衡的特点,Hash索引具有查找速度快的特点
- 索引是关系数据库内部实现技术,属于内模式的范畴
- create index语句定义索引的时候,可以定义索引是唯一索引(学号)或非唯一索引(年龄)或聚簇索引
- 建立索引
- 语法格式:create [unique] [cluster] index<索引名> on <表名>(列名)
- unique 表明此索引每一个索引值对应唯一的数据
- 例如:为学生-课程数据库中的student,course,sc三个表建立索引
- create unique index stusno on student(sno);按学号升序建立唯一索引
- create unique index coucno on course(cno);按课程号升序建立唯一索引
- create unique index scno on sc(sno asc,cno desc);按学号升序和课程号降序建立唯一索引
- cluster表示建立的是聚簇索引,聚簇索引是指索引顺序与表中记录的物理顺序一致的索引组织
- 例如:再student表的sname列上建立一个聚簇索引(聚簇索引查询效率最高)
- create cluster index stusname on student(sname)
- 注意:
- 在最经常查询的列上建立聚簇索引以提高查询效率
- 一个基本表上最多只可以建立一个聚簇索引
- 经常更新的列不适宜建立聚簇索引
- 删除索引:
- 语法格式:drop index(索引名); --删除索引的时候,系统会从数据字典中删去有关该索引的描述
- 例如:删除Student表的stusname索引
- drop index stusname;
3.2.4-数据字典
- 数据字典是关系数据库管理系统内部的一组系统表
- 数据字典记录了数据库中所有的定义信息,包括模式定义,视图定义,索引定义,完整性约束定义,各类用户对数据库的操作权限,统计信息等
- RDBMS执行SQL数据定义的时候,实际就是更新数据字典
*3.3-数据查询
- 语法格式:
select [all/distinct]
目标列
from
表名或视图名列表
where
条件列表
group by
列名
having
分组之后的条件
order by
排序的列名 asc/desc
limit
分页限定
3.4.1-单表查询
- 功能:对一个表的内容进行查询
- 选择表中的若干列
- 查询全体学生的学号和姓名:select sno,sname from student;
- 查询全部列 select * from student;
- 查询结果计算的值
- 算数表达式
- 字符串常量
- 函数 lower(列名) 将列名字符串全部变为小写
- 列别名
- 查询满足条件的元组
- 比较:> < >= <= !>
- 确定范围:between and,not betweenk
-- 查询年龄在【20,23】之间的学生姓名和部门
select
sname,sdept
from
student
where
sage between 20 and 23
- 确定集合:in ,not in
-- 查询信息系,数学系,和计算机系的学生的姓名和性别
select
sname,sdept
from
student
where
dept in('is','ma','cs');
- 字符匹配:like,not like --%和_
-- 查询姓张的学生的详细信息
select
*
from
student
where
sname like '张%';
-- 查询DB_Design课程的课程号和学分
select
cno,credit
from
course
where
cname like 'DB\_Design' ESCAPE '\';
-- ESCAPE '\'表明\是转码字符,将通配符转义为普通字符
-- 查询以'DB_'为开头,且倒数第三个字符为i的课程的细致情况
select
*
from
course
where
cname like 'DB\_%i__' escape '\';
--
- 空值:is null ,is not null
-- 涉及空值的查询,比如,有的学生没有参加某一门考试,如果不对这个null进行处理的话,则该学生的
-- 总成绩也会是null,这是不合理的
select
name,math,english,math+ifnull(english,0)
from
stu2
-- ifnull(X,Y)函数,若X为null,则以Y值替换之
--
- 多重运算(逻辑运算):and,or,not,and的优先级高于or,所以推荐有逻辑运算的时候加上括号
- 取消重复行(distinct),不写则默认all
- select distinct sex from student;
- order by
- 升序asc(默认),降序desc
- 当排序列含空值时,空值为最大值
-- 查询选秀了3号课程的学生的学号以及成绩,查询结果按分数降序排列
select
sno,grade
from
sc
where
cno='3'
order by
grade desc;
-- 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列
select
*
from
student
order by
sdept asc,age desc;
- 聚集函数(所有的都有可选项DISTINCT |all)
- count 统计元组个数
- count(column_name) 函数返回指定列的值的数目(NULL 不计入)
2. count(DISTINCT column_name) 函数返回指定列的不同值的数目
- sum 求和
- avg 求平均值
- max 求最大值
- min 求最小值
- 注意:where中时不可以使用聚集函数作为条件表达式的,聚集函数只可以用在select子句和 group by中的having子句
- group by子句:按指定的一列或多列的值分组,值相等的为一组,来细化聚集函数的作用对象
- 未对查询结果分组,聚集函数将作用于整个查询结果
- 对查询结果分组后,聚集函数将分别作用于每个组
-- 求各个课程号及其相对应的选课人数
select
cno,count(sno) as 人数
from
sc
group by cno
-- 求选修了3门以上课程的学生学号
select
sno
from
sc
group by
sno
having
count(cno)>3
- 注意:一旦使用了分组,那么查询的列,要么时分组时依据的列,要么是聚集函数
- having和where子句的区别:
- 作用对象不同:where作用于基表或者视图,从中选择满足条件的元组,having作用于分组,从中选择满足条件的组,having是和group by连在一起的
- where子句中是不可以用聚集函数作为条件的
-- 查询平均成绩大于等于90分的学生学号和平均成绩
-- 下面是错误写法
select
sno,avg(grade)
from
sc
where avg(grade)>90;
-- 下面是正确写法
select
sno,avg(grade)
from
sc
group by
sno
having
avg(grade)>90;
3.4.2-连接查询(多表查询)
- 等值连接和非等值连接查询:进行连接操作的时候,系统并不会生成一个真正的大表,而是在查询的时候才有,查询完就销毁
- 注意:
- 当连接符号为=的时候为等值连接,其他都是非等值连接
- 连接谓词中的列名称为连接字段,并且各连接字段类型必须是可比的,但名字不必是相同的
-- 查询每个学生以及其选修课程的情况
select
student.*,sc.*
from
student,sc
where
student.sno=sc.sno;
- 连接操作的执行方法(了解):嵌套循环法
- 自身连接:一个表与其自己进行连接
- 注意
- 需要个表起别名以示区别
- 由于所有的属性名都是同名属性,因此必须使用别名前缀
- 例如:查询每一门课的间接先行课,即先修课的先修课
select
t1.cno,t2.cpno
from
course t1,course t2
where
t1.cpno=t2.cno;
- 外连接
- 外连接与普通连接的区别:普通连接操作只输出满足连接条件的元组,而外连接操作以指定表为连接主体,将主体表中不符合连接条件的悬浮元组一并输出
- 分类:
- 左外连接:select 字段列表 from 表1 left out join 表2 on 条件
- 查询的是左表所有数据以及其交集部分
- 右外连接:select 字段列表 from 表1 right out join 表2 on 条件
- 查询的是右表所有数据以及其交集部分
- 多表连接:连接操作是两个以上的表进行连接
-- 查询每个学生的的学号,姓名,选修的课程名以及成绩
select
t1.sno,t1.sname,t2.cname,t3.grade
from
student t1,course t2,sc t3
where
t1.sno=t3.sno and t2.cno=t3.cno
- 嵌套查询:是指将一个查询块嵌套在另一个查询块的where子句或者having子句的条件中查询
- 注意:
- 子查询不可以使用order by子句
- 层层嵌套方式反映了sql语言的结构化
- 有些嵌套查询可以用连接运算代替
- 外层查询(父查询),内层查询(子查询)
- 子查询的不同情况
- 子查询的结果是单行单列的
- 子查询可以作为条件,使用运算符去判断
- – 查询员工工资小于平均工资的人
SELECT * FROM emp WHERE emp.salary
< (SELECT AVG(salary) FROM emp);
- 子查询的结果是多行单列的
- 子查询可以作为条件,使用运算符in来判断
- – 查询财务部和市场部所有的员工信息
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME='财务部' OR NAME='市场部');
- 子查询的结果是多行多列的
- 子查询可以作为一张虚拟表
-- 查询员工的入职日期是2011年11月11日之后的员工信息和部门信息` `SELECT * FROM dept t1,(SELECT * FROM emp WHERE emp.join_date>'2011-11-11') t2 WHERE t1.id=t2.dept_id;
- any和all
- >any 大于子查询的结果的某个值
- \>all 大于子查询结果的全部值
-- 查询非计算机科学系中比计算机科学任意一个学生年龄小的学生姓名和年龄
select
sname,age
from
student
where
age<any(select age from student where dept='cs'
and
sdept<>'cs';
- 带有exists谓词的子查询
- exists为此代表存在量词,带有exists谓词的子查询只返回逻辑真值true或逻辑假值false
-- 查询所有选修了1号课程的学生姓名
select
sname
from
student
where exists
(select * from sc where sno=student.sno and cno='1')
- 使用存在量词后,若内层查询结果为非空,则外层的where子句返回真值;否则返回假值
- 由exists引出的子查询目标列表都用*,因为带exists的子查询只返回真值或假值,给出列名无实际意义
- 不同形式的查询间的替换
- 一些带exists或not exists谓词的子查询不能被其他形式的子查询等价替换
- 所有带in谓词,比较运算符,any和all谓词的子查询都可以用带exists谓词的子查询等价替换
- 上面都太过于抽象,不好理解
- exists就是提交匹配到的
- not exists 就是提交匹配不到的
-- 查询选修了全部课程的学生--
select
sname
from
student
where
sno in
(
select
sno
from
sc
group by
sno
having
count(*)=(select count(*) from course)
);
-- 查找学号为00003的学生没有选修的课程
select
cname
from
course
where not exists
(
select
*
from
sc
where
course.cno=sc.cno and sno='00003'
)
-- 查询与刘晨在同一个系的学生
select
sno,sname,sdept
from
student s1
where exists(
select
*
from
student s2
where
s1.sdept=s2.sdept and s2.sname='刘晨'
);
/*
从这个题目,我们要搞懂exists的执行流程
1.首先,将外层查询的第一行带入子查询
2.若子查询返回真,则输出这一行元组对应的列
3.若子查询返回假,则不输出这一元组
4.代入下一元组,循环往复执行
*/
-- 查询选修了全部课程的学生姓名
- 用exists实现全称量词
- 首先插入一个高中知识:命题的否定(是求补集而不是反面)
- 全称命题的否定:要改成存在命题,例如:
- 所有的矩形都是平行四边形—>存在矩形不是平行四边形
- 每一个素数都是奇数—>存在素数不是奇数
- 存在命题的否定:
- 存在一个实数的绝对值是正数—>所有的实数的绝对值都不是正数
- 存在
- 全称命题转换为同意义的存在命题,就是先进行全称命题的否定,然后再否定就可以
- 所以查询选修了全部课程的学生–>学生选修了全部的课—>存在一门课学生没有选修—>没有一门课是学生没有选修的—>没有一门课是他不选修的
-- 查询选修了全部课程的学生姓名 等价于 没有一门课是他不选修的
select
sname
from
student
where not exists(
select * from course
where not exists(
select * from sc
where sno=student.sno and cno=course.cno
)
)
- 用exists实现逻辑蕴涵p->q=!p或q
-- 查询至少选修了学生201215122选修的全部课程的学生学号
-- 等价于不存在这样的课程y,学生201215122学修了y而学生x没有选
3.3.4-集合查询
- 种类:并操作,交操作,差操作
- 并操作:
-- 查询计算机系的学生以及年龄不大于19岁的学生
-- 方法一
select
sno
from
student
where
age<=19 or dept='cs';
-- 方法二,使用并操作
select sno from student where dept='cs'
union
select sno from student where age<=19;
- union:将多个查询结果合并在一起,并且系统自动去除重复元组
- union all:将多个元组合并在一起的时候,保留重复元组
- 交操作 intersect(mysql无此操作)
-- 查询计算机科学系的学生,且不大于19岁的学生
select * from student where dept='cs'
intersect
select * from student where age<=19;
-- 查询即选修了一号课程,又选修了二号课程的学生
-- 方法一,使用集合操作
select sno from sc where cno=1
intersect
select sno from sc where cno=2;
-- 方法二,使用自身连接
select
t1.sno
from
sc t1,sc t2
where
t1.sno=t2.sno and t1.cno=1 and t2.cno=2;
-- 方法三,嵌套查询:多行单列的查询结果--使用in
select
sno
from
sc
where
cno=1 and sno in (select sno from sc where cno=2)
- 差操作Except
-- 查询计算机系的学生与年龄不大于19岁的学生的差集
-- 方法一:使用差操作
select * from student where dept='cs'
except
select * from student where age<=19;
-- 上述例子其实是在查询:是计算机系的学生,且年龄大于19岁
-- 方法二:and操作 略
- 注意:参加集合操作的各查询结果的列数必须相同,对应的数据类型也要相同
3.4.5-基于派生表的查询
- 就是子查询不仅可以出现在where子句中,也可以出现在from子句中,对应的就是多行多列的子查询,此时子查询生成的表称为临时派生表,成为主查询的查询对象
-- 找出每个学生超过他自己选修课程平均成绩的课程号
SELECT
t1.sno,t1.cno
FROM
sc t1,(SELECT sno,AVG(grade) AS a FROM sc GROUP BY sno) t2
WHERE
t1.sno=t2.sno AND t1.grade>t2.a;
3.5-数据更新
3.5.1-插入数据
- 插入元组:
insert into 表名[<属性列>] values (数据)
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙空','男',7200,'2013-02-24',1);
- 注意:
- into子句:属性列的顺序可以与表中的顺序不一致,没有指定属性列默认插入全部
- values子句:提供的值必须和into子句匹配,值与属性列的个数和值的类型要一致
- 没有插入数据的列,rdbms会自动为其赋空值
- 插入子查询的结果
insert into 表名[<属性列>] select子句
-- 求学生的平均年龄,并且把结果存入表中
-- 1.建表
create table dept_age(
dept char(15), -- 系名
avg_age int -- 学生平均年龄
);
-- 2.插入数据
insert into dept_age (dept,avg_age)
select
dept,avg(age)
from
stduent
group by
dept;
- select子句目标列必须和into子句匹配,值的个数,类型都要一致
3.5.2-修改数据
- 功能:修改指定表中满足where子句条件的元组
- 说明:
- set子句,指定修改方式,修改的列,修改后取值
- where子句,指定要修改的元组,缺省表示修改所有元组,不推荐
- 在执行修改语句的时候会检查修改操作是否破坏表上已经定义的完整性规则
-- 语句格式
update <表名>
set 列名1=数据,列名2=数据,列名3=数据
where 条件;
-- 1.修改一个元组:将学生200215121的年龄改为22岁
update
student
set
sage=22
where
sno='200215121';
-- 2.修改全部元组:将所有学生的年龄增加一岁
update
student
set
sage=sage+1;
-- 3.带子查询的修改语句:将计算机科学系全体学生的成绩清零
update
sc
set
grade=0
where
sno in(
select sno from student where sdept='cs'
);
3.5.3-删除数据
- 功能:删除指定表中满足where子句条件的元组
- 说明:where子句:指定要删除的元组,缺省表示要删除表中的全部元组,但是表的定义还在
-- 1.删除一个元组:删除学号为201015121的学生记录
delete from
student
where
sno=201015121;
-- 2.删除多个元组的值:删除所有学生的选课记录
delete from sc;
-- 3.带子查询的删除语句:删除计算机科学系所有学生的选课记录
delete from
sc
where sno in(
select sno from student where dept='cs'
);
3.6-空值的处理
- 一般有以下的情况:
- 该属性有值,但当前不知道他的具体值
- 该属性不应该有值
- 由于某种原因不便于填写
- 空值的产生:插入的时候没插入该行的数据
- 空值的判断:用is null 或is not null
-- 从student表中找出漏填了的数据
select * from student where sname is null or sex is null or sdept is null;
- 空值的约束条件
- 属性定义的时候,定义not null约束,
- 加了unique约束的列,同样不可以有空值
- 码值不可以取空值
- 空值的算数运算,比较运算与逻辑运算
- 算数运算:空值与另一个值(包括另一个空值)的算数运算结果为空
- 比较运算:空值与另一个值(包括另一个空值)的比较运算结果为unknow
- 逻辑运算:
- unkonw取非还是unknow
- false or unkonw=unknow
3.7-视图
- 视图的特点:
- 视图是虚表,是从一个或多个基本表(视图)导出的表
- 只存放视图的定义,而不存放视图对应的数据
- 表中的数据发生变化,视图中查询出的数据也会发生变化
- 基于视图的操作:查询,删除,受限更新,定义基于该视图的新视图
3.7.1-定义视图
- 建立视图
- 语法格式
-- 1.建立计算机系学生的视图
create view
cs_student -- 视图名
as
select sno,sname,sage from student where sdept='cs'; -- 子查询
-- 2.建立计算机系学生的视图,并要求进行修改和插入操作时仍保证该视图只有计算机系的学生
create view
cs_student -- 视图名
as
select sno,sname,sage from student where sdept='cs'
with check option; -- 对其数据更新的时候要检查其完整性 where sdept='cs'
- 说明:
- 组成视图的属性列名:全部省略或全部指定
- 子查询不允许含有order by子句和distinct短语
- RDBMS执行create view语句的时候只是把视图定义存入数据字典,并不执行其中的select语句
- 在对视图查询的时候,按视图的定义从基本表中将数据查出
- 在第二个例子中,加上了with check option ,所有DBMS对该视图进行更新操作的时候
- 修改操作:自动加上 sdept=‘cs’
- 删除操作:自动加上 sdept=‘cs’
- 插入操作:自动检查sdept属性值是否为cs,如果不是,则拒绝进行该操作,如果没有提供sdept属性值,则自动定义为cs
- 建立基于多个基表的视图
-- 建立信息系中,选修了1号课程的学生视图
create view is_s1(sno,sname,grade)
as select student.sno,sname,grade from student,sc where student.sno=sc.sno and sc.cno='1';
- 基于视图的视图
-- 建立-信息系选修了1号课程且成绩在90分以上的学生视图
create view is_s2
as
select sno,sname,grade from is_s1 where grade>=90;
- 带表达式的视图
-- 定义一个反映出学生出生年份的视图
create view bt_s(sno,sname,sbirth)
as
select sno,sname,2020-sage as 出生日期 from student
- 分组视图
-- 将学生的学号以及他的平均成绩定义为一个视图
create view s_g(sno,gavg)
as
select sno,avg(grade) from sc group by sno;
- 删除视图
- 说明:
- 删除视图语句会从数据字典中删除指定的视图定义
- 如果该视图还导出了其他视图,如果使用cascade,会将那些视图一并删除
- 删除基表的时候,由该基表导出的所有视图都必须显式的使用drop view语句删除
-- 删除视图bt_S
drop view bt_S;
-- 删除视图is_s1,但是is_s1的基础上还导出了视图is-s2,所以要使用级联删除
drop view is_s1 cascade;
3.7.2-查询视图
- RDBMS实现视图查询的方法—视图消解法
- 进行有效性检查
- 转换成等价的对基本表的查询
- 执行修正后的查询
-- 在信息系学生的视图里找到年龄小于20岁的学生
select sno,sage from is_student where sage<20
-- 转换后
-- 因为在创建视图的时候,使用了with check option
select sno,sage from student where sdept='is' and sage<20;
2.视图消解法的弊端
3.7.3-更新视图
3.7.4-视图的作用
- 视图可以简化用户的操作
- 视图使得用户可以以多种角度看待同一数据
- 视图对重构数据库提供了一定程度的逻辑独立性
- 视图可以对机密数据提供安全保护
- 适当的利用视图可以更清晰的表达查询
4-数据库安全性
4.1-数据库安全性概述
- 数据库的一大特点是实现了数据的共享,但是共享就必然带来了数据库的安全性问题,数据库的安全性是指包含数据库以防止不合法使用所造成的数据泄露,更改或破坏,系统安全保护措施是否有效是数据库系统主要的性能指标之一
4.1.1-数据库的不安全因素
- 非授权用户对数据库的恶意存取和破坏(例如黑客)
- 措施:
- 用户身份鉴别
- 存取控制
- 视图
- 数据库中重要或者敏感的数据被泄露
- 措施
- 强制存取控制
- 数据加密存储
- 加密传输
- 安全要求较高的系统提供审计日志分析,对潜在的威胁提前采取措施加以防范
- 安全环境的脆弱性(包括计算机硬件,操作系统,网络系统等的安全性)
- 措施:
- 建立一套可信的计算机系统的概念和标准
4.1.2-安全等级
- TCSEC/TDI安全等级划分----四组七个等级----D,C(C1,C2),B,(B1,B2,B3),A(注意:同级别数字越大越安全,A级最安全)
- 各个安全级别之间向下偏序兼容
- D—dos
- C1—自主存取控制(DAC)—现有的商业系统
- C2—安全产品最低等级—windows2000,oracle7
- B1—标记安全保护—强制存取控制(MAC)
- B2—结构化保护
- B3—安全域—提供数据恢复功能
- A1—验证设计
4.2-数据库安全性控制
- 计算机系统的安全模型–计算机系统中安全措施是一级一级层层设置
- 用户 用户标识和鉴别
- DBMS 数据库安全保护
- OS 操作系统安全保护
- DB 数据密码存储
- 数据库安全性包括:
- 用户身份验证
- 多层存取控制
- 审计
- 视图
- 数据加密等安全技术
- 存取控制流程
- DBMS对提出SQL访问请求的数据库用户进行身份鉴别,防止不可信用户使用系统
- 在sql处理层进行自主存取控制和强制存取控制 ,进一步可以进行推理控制
- 对用户访问行为和系统关键操作进行审计,对异常用户行为进行简单入侵检测
4.2.1-身份鉴别
- 概念: 他是系统提供的最外层安全保护措施
- 组成:用户标识由用户名和用户标识号组成,用户标识在系统整个生命周期内唯一
- 用户鉴别方法:
- 静态口令鉴别:用户自己设置,例如qq密码
- 动态口令鉴别:一次一密,例如手机验证码
- 生物特征鉴别:例如指纹解锁
- 智能卡鉴别
4.2.2-存取控制
- 组成:
- 定义用户权限,并将用户权限等记到数据字典
- 合法权限检查,查找数据字典进行检查
- 常用存取控制方法:
- 自主存取控制(DAC),设置不同的权限(C2级别支持)
- 强制存取控制(MAC),数据对象设置密级,每一个用户也被授予一个许可证(B2级别支持)
4.2.3-自主存取控制方法
- sql通过grant和revoke语句实现自主存取控制
- 用户权限组成:数据对象,操作类型
- 操作对象:数据库模式(模式,基本表,视图,索引),数据(基本表和视图,属性列)
4.2.4-授权:授予与回收
- 权限可以传递with grant option
- grant
grank 权限 on 对象 to 用户 [with grant option]
-- 把查询student表的权限授予给用户u1
grant
select on table student
to
u1;
-- 把对象SC的查询权限授予所有用户
grant
select on table sc
to
public
-- public 代表所有用户
-- 把查询student表和修改学生学号的权限给用户u4
grant
update(sno),select on table student
to
u4;
-- 对属性列的授权的时候必须明确指出相对于的属性列名
-- 把对表sc的inster权限授予给u5,并允许他再把改权限授予别人
grant insert
on table sc
to u5
with grant option;
- 说明:
- 发出grant法人可以是数据库管理员,数据库对象创建者,拥有该权限的用户
- 接受权限的用户可以是一个或多个用户
- with grant option:表明该权限可以再授予其他用户
- sql不允许循环授权
- revoke
- 授予的权限可以由数据库管理员或其他授权者用revoke收回
revoke 权限 on 类型 对象名 from 用户 cascade|restrict
-- 把用户u4修改学生学号的权限收回
revoke update(sno)
on table student
from u4;
-- 收回所有用户对表sc的查询权限
revoke select
on table sc
from public;
-- 把用户u5对sc表的insert权限收回
revoke insert
on table sc
from u5 cascade;
-- 因为使用了cascade,u6和u7的权限一同被收回
- 总结
- 数据库管理员拥有所有对象的所有权限
- 用户的授权:拥有自己建立的对象的全部的操作权限
- 被授予用户的授权:如果有with grant option,则可以继续授权下去
- 所有授予出去的权限都可以使用revoke收回
- 创建数据库模式的权限(数据库管理员再创建用户的时候实现对创建数据库模式的权限)
- 说明:
- 只有系统的超级管理员用户才有权创建一个新的数据库用户
- 新创建的数据库用户有三种权限:connect,resource和DBA,默认connect,该权限下不可以创建新用户,不可以创建模式,不能创建基本表
- resource用户可以创建基本表和视图,称为其属主
- DBA是超级用户
4.2.5-数据库角色
- 角色可以传递with admin option
- 概念:角色是权限的集合,数据库角色是指被命名的一组与数据库操作相关的权限
- 连接一个概念,就是sql中,是先用create role创建角色,然后用grant授予权限,如果我们希望有一百个人拥有查看成绩的权限,则要创建一百个角色,然后授权一百次,这样子太繁琐,我们可以把查看成绩的权限命名为学生,也就是学生角色,这样子我们可以直接创建一百个学生角色,大大简化授权过程,也便于以后修改权限
- 角色创建
- 格式:grant role <角色名>
- 给角色授权
- 格式:grant 权限 on <对象类型,比如可以是一个表>对象名 to 角色
- 将一个角色授予其他的角色或用户
- 格式:grant 角色 to 角色[用户列表] [with admin option]
- 说明:
- 该语句把角色授予某用户,或授予另一个角色
- 授予者是角色创建者或拥有在这个角色上的admin option
- 指定了with admin option则获得某种权限的角色或用户可以把这个权限授予其他角色
- 角色权限收回
- 格式:revoke 权限 on 对象类型 对象名 from 角色
- 说明:
- 用户可以回收角色的权限,从而修改角色拥有的权限
- revokde执行者是:角色的创建者,拥有在这个角色上的admin option
-- 通过角色实现将一组权限授予一个用户
-- 1. 首先创建一个角色R1
create role R1
-- 2. 然后使用grant语句,使角色R1拥有student表的select,update,insert权限
grant select,update,insert
on table student
to R1;
-- 3. 将这个角色授予王平,张明,赵玲,使他们具有角色R1所包含的全部权限
grant R1 to 王平,张明,赵玲;
-- 4. 可以一次性通过R1来回收王平的这三个权限
revoke R1 from 王平;
4.2.6-强制存取控制方法
- 自主存取控制的缺点:
- 可能存在数据’无意泄露’的风险
- 原因:这种机制仅仅通过对数据的存取权限来进行安全控制,而数据本身并无任何安全性标记
- 解决:对系统控制下的所有主客体实施强制存取控制策略
- MAC
- 作用:保证更高强度的安全性,用户不能直接感知或进行控制
- 适用于对数据有严格而固定密级分类的部门,比如军事部门
- 分类:
- 主体:系统中的活动实体,包括DBMS所管理的实际用户和代表用户的各进程
- 客体:系统中的被动实体,包括受主题操纵的文件,表,索引,视图
- 敏感度标记
- 分级
- 绝密ts top secret
- 机密s secret
- 可信c confidential
- 公开p ts>=s>=c>=p
- 主体的敏感度标记称为许可证级别
- 客体的敏感度标记称为密级
- 规则
- 仅当主体的许可证级别大于或等于客体的密级的时候,该主体才可以读相应的客体
- 仅当主体的许可证级别小于或等于客体的密级的时候,该主体才可以写相应的客体
- 说明:
- MAC是对数据本身进行密级标记,无论数据如何复制,标记与数据是一个不可分的整体,只有符合密级要求的用户才可以操纵数据
- 实现MAC,要先实现DAC
- DAC与MAC共同构成了DBMS的安全机制,先DAC检查,后MAC检查
4.3-视图机制
- 视图对数据库安全的作用
- 要把保密的数据对无权存取这些用户的用户隐藏起来,对数据提供一定程度的安全保护
- 间接的实现支持存取谓词的用户权限定义
-- 建立计算机系学生的视图,把对该视图的select权限授予王平,把该视图上的所有操作权限授予张明
-- 1. 先建立计算机系学生的视图cs_student
create view cs_student
as
select * from student where sdept='cs';
-- 2. 在视图上给王平老师授予检索计算机系学生信息的权限
grant select
on cs_studeng
to 王平;
-- 3. 在视图上所有的权限授予系主任张明
grant all priviliges -- [all priviliges代表授予全部权限]
on cs_student
to 张明;
4.4-审计
- 概念:
- 审计日志:启用一个专门的审计日志,将用户对数据库的所有操作记录在上面
- 审计员:审计员利用审计日志监控数据库的各种行为,找出非法存取数据的人,时间和内容
- C2以上安全级别的DBMS必须具有审计功能,因为审计太耗费资源了,比较话费时间和空间,主要用于安全性要求较高的部门
- 审计功能是可选的
- 审计事件
- 服务器事件
- 系统权限
- 语句事件:DDL DML DQL DCL
- 模式对象事件
- 审计功能(略)
- 只允许审计员查阅和转储日志,不允许任何用户新增和修改审计记录,要保证审计记录的真实性和完整性
- 审计分类
- 用户级审计:任何用户可设置,针对自己创建的表和视图进行审计
- 系统级审计:由DBA设置
- audit语句和noaudit语句
- audit设置审计功能,noaudit取消审计功能
-- 对修改sc表结构或修改sc表数据的操作进行审计
audit alter update
on sc;
-- 取消对sc表的一切审计
noaudit alter,update
on sc;
- 说明:
- 审计设置和审计日志一般存储在数据字典中
- 必须打开审计开关
- 数据库安全审计系统提供一种事后检查的安全机制,而DAC和MAC是事前检查的安全机制
4.5-数据加密
- 概述:
- 基本概念:数据加密是防止数据库中数据在存储和传输过程中失密的有效手段
- 基本思想:原始数据(明文)---->算法---->不可直接识别的格式(密文)
- 数据加密包括
- 存储加密
- 传输加密
- 存储加密:
- 分类:
- 透明(怎么加密的无需用户管)存储加密
- 非透明存储加密
- 透明存储加密
- 内核级加密方式
- 在写磁盘时加密,授权用户读取数据时解密(密钥:就是密码本)
- 数据库的应用程序不需要做任何修改,只需在创建表语句中说明需加密的字段即可
- 性能好,安全完备性高
- 非透明存储加密(通过多个加密函数实现)
- 传输加密
- 链路加密
- 在链路层加密
- 传输信息由报文和报头两部分组成,报文和报头均加密
- 端到端加密
- 在发送端加密,接收端解密
- 只加密报文不加密报头
- 所需密码设备数量相对较少,容易被非法监听
- DBMS可信传输步骤
- 创建可信连接(向对方展示自己的数字证书)
- 确认通信双方端点的可靠性
- 协商加密算法和密钥
- 可信传输数据
- 关闭可信连接
4.6-其他安全性措施(略)
4.7-小结
- DBMS是管理数据的核心,必须有套完整而有效的安全性机制
5-数据库完整性
- 基本概述
- 数据库完整性是指数据的正确性和相容性,是防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据,防范的对象是不合语义的,不正确的数据
- 正确性是指数据是符合现实世界语义,反映了当前情况的(不是判断数据是否出错的),例如学生性别只能是男或女
- 数据的相容性是指:数据库同一对象在不同关系表中的数据是符合逻辑的,例如参照完整性
- 数据库的安全性是保护数据库防止恶意的破坏和非法的获取,防范的对象是非法用户��非法操作
- 数据库在完整性方面应该具有的功能
- 提供定义完整性约束条件的机制
- 提供完整性检查方法,一般在insert update delete的时候检查
- 违约处理
- 拒绝(默认处理方式)
- cascade级联
- 设置空值(常用在删除的时候)
5.1-实体完整性
5.1.1-实体完整性的定义
- 主码可以由多个属性
- 单属性的情况下
- 可以定义为列级约束条件
- 可以定义为表级约束条件
- 多属性情况下,只可以表级约束
-- 在列级定义主码,只适用于单属性主码
create table student(
sno char(9) primary key,
sname char(20) not null,
ssex char(2),
sage smallint,
sdept char(20)
);
-- 在表级定义主码
create table student(
sno char(9),
...
sdept char(20),
primary key(sno) -- 表级定义主码,括号内可以有多个属性
);
-- 如果是在sc表中,则必须是表级定义,因为主码是由sno和cno构成的,定义如下
primary key(sno,cno)
5.1.2-实体完整性检查和违约处理
- 完整性检查的内容:RDBMS按照实体完整性规则自动进行检查,内容包括
- 检查主码是否唯一
- 检查主属性是否为空,只要有一个为空就拒绝修改
- 检查主码值的方法
- 全表扫描
- 缺点是十分费时,为避免对基本表全表扫描,RDBMS一般都会在主码上建立一个自动索引,例如B+树索引
5.2-参照完整性
5.2.1-参照完整性定义
-- 定义sc表中的参照完整性
create table sc(
sno char(9) not null,
cno char(4) not null,
grade smallint
primary key(sno,cno), -- 表级定义主码
foreign key(sno) references student(sno), -- 表级定义参照完整性
foreign key(cno) references course(cno) -- 表级定义参照完整性
);
5.2.2-参照完整性检查和违约处理
被参照表(student) | 参照表(SC) | 违约处理 |
可能破坏参照完整性 | 插入元组 | 拒绝(默认策略) |
可能破坏参照完整性 | 修改外码值 | 拒绝 |
删除元组 | 可能破坏参照完整性 | 拒绝/cascade删除/设置为空值 |
修改主码值 | 可能破坏参照完整性 | 拒绝/cascade修改/设置为空值 |
- 定义违约处理
-- 在创建表的时候定义违约处理
create table sc(
sno char(9) not null,
cno char(9) not null,
grade smallint,
primary key(sno,cno),
foreign key(sno) references student(sno)
on delete cascade -- 定义外键sno级联删除
on update cascade, -- 定义外键sno级联更新
foreign key(cno) references course(cno)
on delete no action -- 定义外键cno拒绝删除执行
on update cascade, -- 定义外键cno级联更新
);
5.3-用户定义完整性
5.3.1-属性上的约束条件
- 定义包括:
- 列值非空(not null)
- 列值唯一(unique)
- 检查列值是否满足一个条件表达式(check)
create table test(
sno char(9) not null,
cno char(9) not null, --若cno是主码,可以不设置not null
dname char(9) unique not null, -- 设置唯一且非空
ssex char(2) check(ssex in('男','女')), -- 性别只可以取男或女
grade smallint check(grade>=0 and grade<=100>) -- 成绩的取值范围是0-100
)
- 属性上的约束条件检查和违约处理:插入或修改的时候,RDBMS检查属性上的约束条件是否满足, 如果不满足则拒绝执行
5.3.2-元组上的约束条件
- 元组上约束条件的定义
- 在create table时可以使用check短语定义元组上的约束条件,即元组级的限制
- 元组级的限制可以设置不同属性之间的取值相互约束条件
-- 当学生的性别时男时,其名字不可以是Ms.打头
create table student(
sno char(9),
same char(8) not null,
...
primary key(sno),
check(ssex='女' or sname not like 'Ms%') --定义了元组中sname和ssex两个属性值之间的约束条件
)
- 元组上约束条件检查和违约处理:不满足则拒绝执行
5.4-完整性约束命名子句
- 完整性约束命名子句
- 格式:constraint 完整性约束条件名 完整性约束条件
- 完整性约束条件包括:not null,unique,primary key,foreign key,check短语等
-- 建立学生登记表student,要求学号在90000-99999之间,姓名不可以取空值,年龄小于30,性别只可以是男或女
create table student(
sno numeric(6) student constraint c1 check(sno between 90000 and 99999)
sname char(20) constraint c2 not null,
sage numeric(3) constraint c3 check(sage<30),
ssex char(2) constraint c4 check(ssex in('男','女')),
constraint c5 primary key(sno)
);
- 修改表中的完整性限制:使用alter table语句修改表中的完整性限制,一般常用方式是删了原来的,再建立一个新的约束条件
-- 去掉上例中对性别的限制
alter table student
drop constraint c4;
-- 修改上例student中的约束条件,要求学号改为在900000-999999之间,年龄由小于30改为小于40
-- 可以删除原来的,再增加新的约束条件
alter table student drop constraint c1;
alter table student add constraint c1 check (sno between 900000 and 999999),
alter table student drop constraint c3;
alter table student add constraint c3 check(sage<40)
5.5-域中的完整性限制(*标识,略)
- sql可以使用create domain语句建立一个域以及域应该满足的完整性约束条件,然后用域定义属性
-- 建立一个性别域,并声明性别域的取值范围
create domain GenderDomain char(2) check(value in('男','女'));
-- 可以再创建表的时候使用GenderDomain定义性别属性
ssex GenderDomain,
-- 建立一个性别域,并对其中的限制命名
create domain GenderDomain char(2)
constraint c1 check(value in('男','女'))
-- 删除域GenderDomain的限制条件c1
alter domain GenderDomain
drop constraint c1;
-- 在域GenderDomain上增加性别的限制条件c2
alter domain GenderDomain
add constraint c2 check(value in('1','0'));
5.6-断言
- 创建断言的语句格式:create assertion 断言名 check子句
- 每个断言都被赋予一个名字,check子句中的约束条件与where子句的条件表达式类似
-- 限制数据库课程最多60名学生选修
create assertion a1 check(
60>=select count(*) from course,sc where sc.cno=course.cno and course.cname='数据库')
);
-- 限制每一门课最多60名学生选修
create assertion a2 check(
60 >= all (select count(*) from sc group by cno)
);
-- 限制每个学期,每一门课程最多60名学生选修
-- 1. 首先,需要修改sc表的模式,增加一个'学期(TERM)'属性
alter table sc add term date;
-- 2. 然后,定义断言
create assertion a2 check(60>= all (select count(*) from sc group by cno,term));
- 删除断言
- 语句格式:drop assertion 断言名;
- 如果断言很复杂,则系统在检测和维护断言的开销是较高的,这是在使用断言的时候应该注意的
5.7-触发器
- 概念:触发器(Trigger)是用户定义在关系表上的一类由事件驱动的特殊过程
- 说明:
- 触发器保存在数据库服务器中
- 任何用户对表的增删改操作均由服务器自动激活相应的触发器
- 触发器可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制能力
5.7.1-定义触发器
- 触发器又叫做事件-条件-动作规则
- 语句格式为
create trigger 触发器名
{before|after} 触发事件 on 表名 -- before和after 指触发器执行是在触发事件之前还是之后,触发事件一般是增删改
referencing new|old row as 变量 -- new|old代表修改前还是后的数据 ,赋予给变量
for each{row|statement} -- row|statement代表是行还是语句
[when 触发条件]触发动作
- 说明:当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执行规则中动作,否则不执行该动作,规则中的动作可以很复杂,通常是一段sql存储过程
- 定义触发器的语法说明
- 表的拥有者才可以在表上创建触发器
- 触发器名
- 可以包含模式名,也可以不包含模式名
- 统一模式下,触发器名必须是唯一的
- 触发器名和表名必须在同一模式下
- 表名
- 触发器只可以定义在基本表上,不可以定义在视图上
- 当基本表的数据发生变化时,将激活定义在该表上相应触发事件的触发器
- 触发事件
- insert,delete,update,也可以是这几个事件的组合
- update of<触发列,…>,即进一步指明修改哪些列时激活触发器
- alter和before时触发的时机,alter表示在触发事件执行之后激活触发器
- 触发器类型
- 行级触发器(for each row) 和语句级触发器(for each statement)
- 例如:在上例建立Teacher中,表上创建一个alter update触发器,触发事件是update语句
update teacher set deptno=5;
假设该表有1000行
如果是语句级触发器,那么执行完该语句后,触发动作会发生一次,如果是行级触发器,触发动作将会执行1000次
- 触发条件:
- 触发器被激活的时候,只有当触发条件为真时,触发动作体才执行,否则触发动作体不执行
- 如果省略when触发条件,则触发动作体在触发器激活后立刻执行
-- 当对表sc的grade属性进行修改时,若分数增加了10%,则将此次操作记录到下面的表中:
-- sc_u(sno,cno,oldgrade,newgrade)
-- 其中,oldgrade是修改前的分数,newgrade是修改后的分数
-- 设计触发器
create trigger sc_t -- 创建一个触发器,触发器名为:sc_t
after update of grade on sc -- 当sc表上对grade属性发生update后,激活触发器
referencing
old row as OldTuple -- 将旧的行赋予变量名:OldTuple
new row as NewTuple -- 将新的行赋予变量名:NewTuple
for each row -- 行级触发器
when(NewTuple.grade>=1.1*OldTuple.grade) -- 当新行中的成绩是旧行中成绩的1.1倍时,执行触发器
insert into sc_u(sno,cno,oldgrade,newgrade) -- 下面就是一个插入语句,将指定的信息插入到sc_u表内
values(OldTuple.sno,OldTuple.cno,OldTuple.grade,NewTuple.grade)
-- 将每次对表student的插入操作所增加的学生个数记录到表studentInsertLog中
create trigger student_count
after insert on student
referencing
new table as delta
for each statement
insert into studentInsertLog(numbers)
select count(*) from delta
5.7.2-激活触发器
一个数据表上可能定义了多个触发器,遵循如下的顺序
- 执行该表上的before触发器
- 执行激活触发器的sql语句
- 执行该表上的after触发器
5.7.3-删除触发器
删除触发器的语法:
drop trigger 触发器名 on 表名
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除
6-关系数据库理论
6.1-函数依赖
- 一些术语和记号
- x->y,但是同时,y不属于x,则称x->y是非平凡的函数依赖 例如:(sno,cno)->grade
- 若y属于x,则x->y是平凡函数依赖 例如:(sno,cno)->sno
- 对于任一关系模式,平凡的函数依赖是一定存在的,所以一般我们讨论非平凡的函数依赖
- 若x->y,则x称为这个函数依赖的决定属性组,也称为决定因素
- 若x->y,且y->x,则x<–>y
- 完全函数依赖:若x->y,同时x的任意真子集,都无法推出y,记作x-F->y
- 若x->y,但y不完全函数依赖于x,则称为部分函数依赖,记作x-P->y
- 传递函数依赖:x->y(非平凡函数依赖),y!->x,y->z(非平凡函数依赖),则称z对x传递函数依赖,记为x-传递->z
- 候选码:设k为R<U,F>中的属性或属性组合,若U完全函数依赖于k,则称k为R的一个候选码
- 超码:u部分函数依赖于k,则称k为超码,候选码是最小的超码
- 概念:设计数据库的时候,需要遵循的一些规范。
- 设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求成为不同的范式,各种范式呈递次规范,越高的范式,数据库冗余越小
- 规范化:是指一个低一级范式的关系模式,通过模式分解转换为若干个高一级范式的关系模式集合的过程
- 目前关系数据库有六种范式:学生表(学号,姓名,系名,系主任,课程名称,分数)
- 第一范式(1NF)
- 每一列都是不可分割的原子数据项
- 缺陷:
- 存在非常严重的数据冗余
- 数据添加存在问题
- 删除数据也存在问题
- 修改复杂
- 第二范式(2NF)
- 在1NF的基础上,非码属性必须完全依赖于候选码(在1NF的基础上消除非主属性对主码的部分函数依赖,即非主属性要完全依赖于主属性)
- 函数依赖:如果通过A的属性(属性组)的值,可以唯一确定B属性的值,则称B依赖于A A->B
- 如:学号—>姓名 (学号,课程名称)—>分数
- 完全函数依赖:如果A是一个属性组,则B属性值的确定需要依赖于A属性组中的全部属性
- 如:(学号,课程名称)—>分数
- 部分函数依赖:如果A是一个属性组,则B属性值的确定只需要依赖于A属性组中的部分属性
- 如:(学号,课程名称)—>系名,例如学生表中,候选码为学号和课程名称,但是存在部分函数依赖,例如系名,系主任,姓名依赖于学号而不依赖于(学号,课程名称)
- 传递函数依赖:A—>B B—>C,如果通过A属性的值可以唯一确定B属性的值,再通过B属性的值可以唯一确定C属性的值,则称C传递函数依赖于A
- 如:学号—>系名 系名—>系主任
- 总结,所以我们应该进行表的拆分,将部分函数依赖的属性拿出来单独组成一个表将学生表拆分为选课表(学号,课程名称,分数)和学生表(学号,姓名,系名,系主任),此时,函数中不存在非主属性对主属性的部分函数依赖了
- 缺陷:
- 数据添加存在问题
- 数据删除存在问题
- 第三范式(3NF)
- 在2NF的基础上,任何非主属性不依赖于其他非主属性(在2NF的基础上消除传递依赖)
- 我们发现了问题,就是进行拆分后的学生表,存在传递依赖,学号–>系名 系名–>系主任,所以我们应该添加一个系表(系名,系主任)
- 3NF的不彻底性可能表现在存在主属性对码的部分依赖和传递依赖
- 巴斯—科德范式(BCNF)
- 在3NF的基础上,任何非主属性依赖不能对主键子集依赖(在3NF基础上消除对主码子集的依赖)
- 换言之:如果每一个属性集都包含候选码,则关系R就是BCNF
- 判断方式
- 若R中只有一个候选码,若R是3NF,则R必为BCNF
- 若R中有多个候选码,则逐一判断每一个函数依赖x中的决定因素是否包含候选码,成立则属于BCNF
- 若候选码为全码,则属于BCNF
- 性质:
- 所有非主属性都完全依赖于每个候选码
- 所有主属性都完全函数依赖于每个不包含它的候选码
- 没有任何属性完全依赖于非码的任何一组属性
- 如果一个关系数据库中所有的关系模式都属于bcnf,那么在函数依赖范围内,已经实现了模式的彻底分解,达到了最高的规范化程度,消除了插入异常和删除异常
- 第四范式(4NF)
- 关系模式R<U,F>属于1NF,如果对于R的每个非平凡多值依赖,X->->Y,X都含有码,则R<U,F>属于4NF
- 第五范式(5NF 又称完美范式)
- 了解一些课本知识
- 首先说明,键字=码字,所以主关键字=主码=主键,候选键=候选码=候选关键字
- 各种码:
- 候选码/超级玛:可以唯一的表示一个元组的属性或者属性集,一个表可以有多个候选码,例如学生表(学号,姓名,身份证),那么学号可以作为候选码,身份证也可以作为候选码
- 候选码是没有多余属性的,例如(学号,身份证)不可以作为候选码,因为只需要一个就可以唯一的标识一个元组
- 最小性:任一候选键的任何真子集都不可以唯一的标识一个记录
- 非空性
- 标识性
- 主码:从候选码中人为的挑选一个作为主码,例如上面的学生表中,我们可以选择学号作为主码,也可以选择身份证号作为主码
- 唯一性
- 标识性:一个表的所有记录都具有不同的主码取值
- 非空性
- 主属性:包含在任意候选码中的属性为主属性,例如学号和身份证就是主属性
- 非主属性:姓名就是非主属性
- 全码:当所有的属性共同构成候选码的时候,称该码为全码
- 码/超键/键:可以唯一标识一条记录的属性或者属性集,即含有候选码就行,例如(学号,姓名)是一个码,(姓名,身份证)是一个码。
- 表
- 一个表就是一个关系
- 表中一行为一个元组
- 一列为一个属性,要和属性名区别开
- 元组中的一个属性值为分量(关系必须规范化:分量不可再分)
- 关系名就是表名
- 关系模式就是表头
6.2-多值依赖
- 定义:设R(U)是属性集u上的一个关系模式,X,Y,Z是U的子集,并且Z=U-X-Y.关系模式R(U)中多值依赖X->->Y成立,当且仅当对R(U)的任意关系r,给定一对(x,z)的值,有一组Y的值(和函数依赖不一样,不是唯一确定一个值,而是一组值),而这组值仅仅取决于x值而与z值无关
- 例如:Teaching(C课程,T教师,B参考书)中,对于C的每一个值,T有一组值与之对应,而不论B取何值,因此T多值依赖于C,即C->->T
- 多值依赖和函数依赖的区别:
- 多值依赖的成立于属性集的范围有关,而函数依赖无关
- 若x->y在U上成立,且c属于y的子集,则x->c也成立,但是在多值依赖中,若x->->y,且c属于y,则不能确定x->->c
6.3-数据依赖的公理系统
- Armstrong公理系统是一套推理规则,是模式分解算法理论的基础,主要用于求给定关系模式的码,从一组函数依赖求得蕴涵的所有函数依赖,该公理系统包含以下的1,2,3
- 自反率:由自反率所得到的函数依赖均是平凡的函数依赖,自反率的使用并不依赖于F
- 增广率:若X->Y为F所蕴涵,且Z属于U,则XZ->YZ为F所蕴涵
- 传递率:若X->Y,Y->Z为F所蕴涵,则X->Z为F所蕴涵
- 由1,2,3可以得出下面三条推理规则:
- 合并规则:X->Y,X->Z==>X->YZ
- 伪传递规则:X->Z,WY->Z,XW->Z
- 分解规则:X->Y,且Z属于Y,则X->Z
- 由合并规则和分解规则,可以得出一个引理:X->A1A2A3…An的充分必要条件是X->A1,X->A2…X->An
- 在关系模式R<U,F>中,为F所逻辑蕴涵的函数依赖的全体叫做F的闭包,记作F+
- 如果题目要求,求出(AB)+,那么首先,将(AB)分成A,B,AB,然后在函数依赖全体中找,是否有函数依赖的左边为A,B,AB,如果有,就将他的右边写出,例如A->C,AB->E,那么我们可以将CE加入AB,那么下一步,我们应该将(ABCE)分为A,B,C,E,AB,AC,AE,BC,BE…然后去函数依赖集中找左边是否有相等的,然后把右边的属性写出并加入,结束条件是有两个,1是发现这一次的结果和上一次一样,那么说明已经无法扩充了,停止,2是发现当前已经包含了全部属性,那么也可以停止了
- 注意:我们将(AB)分成A,B,AB,如果函数依赖集中有一个AC->E,那么我们无法将这个E作为结果,因为我们要求左边必须一摸一样
6.4 模式分解
- 模式分解
- 若要求分解保持函数依赖,那么模式分解总可以达到3nf,但是不一定可以达到BDNF
- 若要求分解保持函数依赖,又具有无损连接性,那么可以达到3nf,但是不一定可以达到BDNF
- 若要求分解具有无损连接性,那一定可以到达4NF
- 首先,对模式进行分解,首先要求出最小函数依赖集
- 方法保持函数依赖进行分解
- 首先,先观察函数依赖,将函数依赖中没有出现过的属性拿出来,单独组成一个表,然后我们之后只考虑剩下的属性
- 然后观察函数依赖,若存在一个函数依赖,他的箭头左右的属性加起来,就是全部属性,那么停止分解,该表是无法分解的
- 之后,将函数依赖中,箭头左边的属性相同的,划分为组,组中出线过的元素,成为一个表
- 即保持函数依赖,又保持无损连接
- 首先,先按保持函数依赖进行分解
- 判断出该表的候选码是谁
- 如果,按照函数依赖进行分解的表中,存在一个表,包含候选码,或者表的全体元素是候选码的一个真子集,那么,我们就保持原分解不变,原分解在保持函数依赖的同时,已经保持了无损连接
- 如果不存在这样子的表,那么就在保持函数依赖分解的基础上,将候选码的属性组成一个表,加入
- 保持无损连接的分解
- 首先先判断关系模式中的表是否是BCNF,如果是,那么就不需要进行分解,算法结束
- 如果不是BCNF,那么肯定存在这样子的函数依赖,他的决定因素是不包含码的,我们将这样子的函数全部取出来,对于每一个函数依赖,将箭头左右的属性合并成一个表,最后取全部属性,减去在之前的函数依赖的箭头右边出现的属性,再组成一个表,然后将这些表组成一个分解,返回1进行判断
- 判断候选码的:
- 如果有属性不在函数依赖集中出现,那么它必须包含在候选码中;
- 如果有属性只在函数依赖集右边出现,那么它必不包含在候选码中;
- 如果有属性只在函数依赖集的左边出现,则该属性一定包含在候选码中。
- 如果有属性或属性组能唯一标识元组,则它就是候选码,也就是说,通过函数依赖所求出的候选码的闭包中,能够包含所有的属性。
7-数据库设计
7.1-数据库设计概述
- 规范设计法:
- 基本思想:过程迭代和逐步求精
- 典型方法:
- 新奥尔良法:把数据库设计分为四个阶段,即数据库设计的前四个阶段
- 基于E-R模型的数据库设计方法(常用)
- 3NF的设计方法
- 面向对象的数据库设计方法
- 统一建模语言(UML)方法
7.1.3-数据库设计的基本步骤
- 数据库设计分为六个阶段
- 需求分析:分析应用程序对数据和处理的要求
- 需求收集和分析
- 概念结构设计:用ER图或uml图把这个数据库大致画出来
- 设计概念结构
- 逻辑结构设计:转换规则,数据库管理系统功能,优化方法
- 设计逻辑结构
- 数据模型优化
- 物理结构设计
- 设计物理结构
- 评价设计,性能预测,若不满意,回退到逻辑或物理结构设计
- 数据库实施
- 物理实现
- 实验性运行 不满意则回退到物理结构设计
- 数据库运行和维护
- 说明:
- 需求分析和概念设计独立于任何数据库管理系统
- 逻辑设计和物理设计与选用的数据库管理系统密切相关
- 参加数据库设计的人员
- 系统分析人员和数据库设计人员->参加全过程其水平决定了数据库系统的质量
- 数据库管理员和用户代表(甲方):参加需求分析和数据库的运行和维护
- 应用开发人员:包括程序员和操作员,在实施阶段参与进来,分别负责编制程序和准备软硬件环境
- 各阶段主要任务
- 需求分析阶段:了解用户需求,该阶段是否做的充分与准确,决定了构建数据库的速度和质量
- 综合各个用户的应用需求
- 概念设计阶段:对用户需求进行综合,归纳与抽象(E-R/UML,将用户需求图形化),形成一个独立于具体数据库管理系统的概念模型
- 形成独立于机器特点,独立于各个数据库管理系统产品的概念模式(ER图)
- 逻辑设计阶段:将概念模型转换为数据库管理系统所支持的数据模型,并对其进行优化(把表和视图创建出来,并优化范式)
- 首先将ER图转换成具体的数据库产品支持的数据模型,如关系模型,形成数据库逻辑模式
- 然后根据用户处理的要求,安全性的考虑,在基本表的基础上再建立必要的视图,形成数据的外模式
- 物理结构设计阶段:逻辑数据结构选取一个最适合应用环境的物理结构,包括存储结构和存取方法
- 进行物理存储安排,建立索引,形成数据库内模式
- 数据库实施阶段:根据逻辑设计和物理设计的结果构建数据库,编写与调试应用程序,组织数据库并进行试运行
- 数据库运行和维护阶段:经过试运行后即可正式投入运行,在运行过程必须不断对其进行评估,调整与修改
- 说明
- 设计一个完善的数据库应用程序,往往是上述六个步骤的不断重复
- 设计步骤既是数据库设计的过程,也包括了数据库应用系统的设计过程
- 把数据库的设计和数据处理的设计紧密结合,将这两个方面的需求分析,抽象,设计,实现在各个阶段同时进行
- 各个阶段的数据设计描述
设计阶段 | 设计描述 |
需求分析 | 数据字典,全系统中数据项,数据结构,数据流,数据存储的描述 |
概念结构设计 | 概念模型(ER图),数据字典 |
逻辑结构设计 | 某种数据模型,比如关系型数据模型和非关系(树,网)数据模型 |
物理结构设计 | 存储安排,存储方法选择,存储路径选择 |
数据库实施 | 创建数据库模式create…,装入数据,数据库试运行 |
数据库运行和维护 | 性能监测,转储/恢复,数据库重组和重构 |
7.2-需求分析
- 数据字典:是关于数据库中数据的描述,即元数据,不是数据本身.数据字典在需求分析阶段建立,是进行详细的数据手机和数据分析得到的,并在数据库设计过程中不断修改,充实,完善,必须强调用户的参与
- 数据字典的内容:数据项,数据结构,数据流,数据存储和处理过程
- 数据项:数据项是不可再分的数据单位
- 数据结构:数据结构反映了数据之间的组合关系
- 数据结构描述={数据结构名,含义说明,组成:{数据项或数据结构}}
- 一个数据结构可以由若干个数据项组成,也可以由若干个数据结构组成,或由若干个数据项和数据结构混合而成
- 数据流:是数据结构在系统内传输的路径
- 数据存储:是数据结构停留或保存的地方,也是数据流的来源和去向之一
- 处理过程:处理过程的具体处理逻辑一般用判定表或判定树来描述,数据字典中只需要描述处理过程的说明性信号
7.3-概念结构设计
- 概念模型的描述工具:E-R模型
- ER图:ER图提供了表示实体型,属性和联系的方法
- 实体性:矩形
- 属性:椭圆
- 联系:菱形联系也可以由属性,和有关实体型连接,无向边上写上联系的类型(一对一,一对多,多对多多对多之间的联系往往是有属性的),实体间可以有多个联系,并不局限于一个联系
- 概念结构设计
- 实体与属性划分原则:为了简化ER图,能作为属性的,尽量作为属性
- 两条准则:
- 作为属性,不能再具有需要描述的性质.属性必须是不可分的数据项,不可以包含其他属性,其实一个实体就是一个二维表,属性不可分就是为了满足1NF
- 属性不可以和其他实体具有联系,即ER图中所表示的是实体间的联系
- ER图的集成
- 合并ER图,生成初步ER图,各个子系统肯定存在去多不一致的地方,大致分为三类冲突
- 属性冲突
- 属性域冲突,例如零件号,有的设置为整形,有的设置为字符型
- 属性取值单位冲突:零件的质量有的以kg为单位,有的以斤为单位
- 命名冲突
- 同名异意
- 异名同意
- 命名冲突
- 结构冲突
- 同一对象在不同的应用中具有不同的抽象,比如职工,在有的应用中是属性,在有的应用中是实体
- 同一实体在不同子系统的ER图中所包含的属性个数和属性排列次序不完全相同
- 消除不必要的冗余,设计出基本ER图,但是有时候冗余的存在也可以提高效率,所以要具体问题具体分析
7.4-逻辑结构设计
- 把ER图变成表
- 转换规则:一个实体型转换为一个关系模式,关系的属性为实体的属性,关系的码为实体的码
- 联系的转换:实体的联系有三种情况
- 一对一联系
- 转换为独立的关系模式
- 关系的属性:与该联系相连的各实体的码及联系本身的属性
- 关系的候选码:每个实体的码均是该关系的候选码,从里面挑一个作为主码就行
- 与任意一端对应的关系模式合并
- 合并后关系的属性:加入未合并关系的码和联系本身的属性
- 合并后关系的码:不变
- 一对多联系
- 转换为独立的关系模式
- 关系的属性:与该联系相连的各实体的码以及联系本身的属性
- 关系的候选码:n端实体的码
- 与n端对应的关系模式合并(可以减少系统中关系个数,比较推荐)
- 关系的属性:在n端关系中加入一端关系的码,和联系本身的属性
- 关系的候选码:不变
- 多对多联系
- 只可以将联系变为一个关系模式
- 关系的属性:与之相连的实体的码和联系本身的属性
- 关系的候选码:与之相连的实体的码构成该关系的码
- 具有相同码的关系模式可以合并,可以减少系统中关系的个数
- 将其中一个关系模式的全部属性加入到另一个关系模式中
- 去掉其中的同义属性
- 适当调整属性的次序
- 三个或三个以上实体间的一个多元联系转换为一个关系模式
- 关系的属性:各实体的码及联系本身的属性
- 关系的候选码:各实体码的组合
- 数据模型的优化
- 优化的方法:
- 确定数据依赖
- 对各个关系模式之间的数据依赖进行极小化处理,消除冗余的联系
- 看是否存在部分函数依赖,传递函数依赖,多值依赖,确定各关系模式属于第几范式
- 按照需求分析阶段得到的各种应用对数据处理的要求,确定是否要进行关系的合并和分解(,效率是第一位的,不是规范化程度越高越好,因为表太多的时候会有很多的连接操作,而连接操作的代价是很高的,有时候,第一范式甚至是第二范式也许是合适的)
- 对关系模式进行必要的分解(常用水平分解和垂直分解)
- 水平分解:把基本关系中的元组分为若干子元组,定义每一个子元组为一个关系(其实就是按属性名group by)
- 要符合28原则,把经常使用的百分之20的元组分成一个关系
- 使每个事务存取的数据对应一个子关系
- 垂直分解:把关系模式R的属性分解为若干子集合,形成若干关系模式
- 分解原则:经常在一起使用的属性从R中分解出来
- 分解优点:可以提高某些事务的效率
- 分解缺点:可能使得一些事务不得不执行连接操作
- 适用范围:取决于分解后R上的所有事务的总效率是否得到了提高
- 设计用户子模式
- 使用更符合用户习惯的别名,比如date-age起别名为出生年份,还比如说消除命名冲突
- 针对不同级别的用户定义不同的视图,以保证系统的安全性
- 简化用户对系统的使用:比如将复杂的查询定义为一个视图
7.5-物理结构的设计
- 内容:为关系模式选取存取方法(建立存取路径)以及设计关系,索引等数据库文件的物理存储结构
- 常见的存取方法:
- B+树索引存取方法
- Hash索引存取方法:如果一个关系的属性主要出现在等值连接条件中或主要出现在等值比较选择条件中,而且满足下面条件之一:
- 该关系的大小可知,而且不变(适用于表固定,不在改变的情况)
- 该关系的大小动态改变,但是所选用的数据库管理系统提供了动态Hash存取方法
- 聚簇存取方法:是为了提高某个属性或属性组的查询速度,把这个或这些属性(称为聚簇码)上具有相同值的元组,存放在连续的物理块中称为聚簇.
- 聚簇对于某些类型的查询,可以提高查询效率
- 在一个基本表中最多只可以建立一个聚簇索引
- 聚簇索引的适用条件
- 很少对表进行增删改操作
- 很少对其中的变长列进行修改操作
- 选择聚簇索引方法:
- 设计候选聚簇
- 常在一起进行连接操作的关系可以建立组合聚簇
- 如果一个关系的一组属性经常出现在相等比较条件中,单个关系可以建立聚簇
- 如果一个关系的一个(或一组)属性上的值重复率很高,则此单个关系可建立聚簇
- 检查候选聚簇中的关系,取消其中不必要的关系
- 从聚簇中删除经常进行全表扫描的关系
- 从聚簇中删除更新操作远多于连接操作的关系
- 从聚簇中删除重复出现的关系,当一个关系中同时加入多个聚簇时,必须从这多个聚簇方案(包括不建立聚簇的方案),从中选择一个,即在这个聚簇上运行各种事务的总代价最小
- 数据库的重组织与重构造
- 重组织:数据库运行一段时间后,由于不断进行增删改操作,会使得数据库的物理存储变坏,降低数据库存储空间的利用率和数据的存取效率,使得数据库的性能下降
- 数据库的重组织不会改变原设计的数据逻辑结构和物理结构
- 重组织的方法:按原设计的要求,重新安排存储位置,回收垃圾,减少指针链
- 重构造:数据库应用环境改变,会导致实体和实体间的联系也发生相应的变化,使得原有的数据库设计不能很好的满足新的需求
- 数据库的重构造,需根据新环境调整数据库的模式和内模式(物理结构和逻辑结构)
- 重构造的方法:增加或删除某些数据项,改变数据项的类型等等,若应用变化太大,已无法通过重构数据库来满足新的需求,则说明当前的数据库应用系统的生命周期已经结束,应该设计新的数据库应用系统了
8-数据库编程
8.1-嵌入式SQL
- sql语言的特点之一是,在交互式和嵌入式两种不同的使用方式下,SQL的语法结构基本一致,在程序设计的环境下,对SQL语句进行必要的扩充.
8.1.1-嵌入式SQL的处理过程
- 主语言:如果将sql嵌入到java中,则java称为宿主语言或主语言,为了区分sql语言与主语言,所有的SQL语句必须加前缀
- c语言:EXEC SQL sql语句
- java语言:#SQL sql语句
- 嵌入式SQL处理过程:预编译方法
- 含嵌入式SQL语句的主语言程序
- 关系数据库管理系统预处理程序转换嵌入式SQL语句为函数调用,就是所有部分都变成主语言形式了
- 转换后的主语言程序
- 主语言编译程序编译处理
- 目标语言程序
8.1.2-嵌入式SQL与主语言之间的通信
- 嵌入式SQL语言中会有两种不同计算模型的语句:SQL语句复杂操纵数据库,高级语言负责控制逻辑流程
- 数据库工作单元与源程序工作单元之间的通信
- 向主语言传递SQL语句的执行状态信息,使得主语言能够据此控制程序流程,主要用SQL通信区实现
- 主语言向SQL语句提供参数,主要用主变量
- 将SQL语句查询数据库的结果交给主语言处理,主要用主变量和游标实现
- 通信区的定义:EXEC SQL INCLUDE SQLCA;
- SQLCA中设置变量SQLCODE,存放每次执行SQL语句后返回的结果的代码
- 当SQLCODE等于success时,表示sql语句成功,否则出错
- 应用程序每执行完一条语句,都应该测试一下SQLCODE的值,以了解该sql语句的执行情况,并做相应的处理
- 主变量:嵌入式sql语句中可以使用主语言的程序变量来输入或输出数据,在SQL语句中使用的主语言程序变量,简称为主变量
- 输入主变量:由应用程序对其赋值,sql语句负责引用
- 输出主变量:由sql语句对其赋值或设置状态信息,返回给应用程序
- 指示变量:是一个整型变量,一个主变量可以附带一个指示变量,用来指示所指主变量的值或条件
- 定义主变量和指示变量的格式
begin declare section
...
...(说明主变量和指示变量,主语言怎么定义变量,这里就怎么写)
...
end declare section
- 说明
- 定义的主变量可以在sql语句中任何一个可以使用表达式的地方出现 比如where 学号=001可以写成where 学号=sno
- 为了与数据库对象名区别,sql语句中的主变量前要加冒号:作为表示 ,所以上例应该改为where 学号=:sno
- 指示变量必须要紧跟在所指主变量之后,指示变量前也必须加冒号标志.
- 在sql语句之外(主语言语句中),可以直接引用主变量和指示变量,不必加冒号
- 游标:
- 为什么使用游标:sql是面向集合的,一条sql语句原则上可以产生或处理多条记录,主语言是面向记录的,一组主变量一次只可以存放一条记录,仅使用主变量并不能完全满足sql语句中向应用程序输出数据的要求,嵌入式sql引入了游标的概念,用来协调这两种不同的处理方式
- 什么是游标:游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区有一个名字,用户可以通过sql语句逐一从游标中获取记录,并赋值给主变量,交由主语言进一步处理
- 建立和关闭数据库连接:
-- 依次检查学生记录,交互式更新某些学生的年龄
EXEC SQL BEGIN DECLARE SECTION;-- 主变量说明开始,以下声明的都是主变量
char Deptname[20]; -- 系名
char HSno[9]; -- 学号
char HSname[20]; -- 姓名
char HSsex[2]; -- 性别
int HSage; -- 旧的年龄
int NEWAGE; -- 要设置的新的年龄
EXEC SQL END DECLARE SECTION; -- 主变量的声明结束
long SQLCODE; -- 存放每次执行SQL语句后返回的结果的代码
EXEC SQL INCLUDE SQLCA; -- 定义sql通信区
int main(void){ /*c语言主程序开始*/
int count=0;
char yn; //
printf("please choose the department name(CS/MA/IS");
scanf("%s",Deptname); //为主变量deptname赋值
EXEC SQL CONNECT TO TEST@localhost:54321 USER "SYSTEM/MANAGER" ; //连接数据库
// 意思就是要用SYSTEM/MANAGER用户访问localhost计算机上的TEST数据库
}
EXEC SQL DECLARE SX CURSOR FOR -- 定义一个名字叫SX的游标
select Sno,Sname,Ssex,Sage -- 定义游标对应的SQL语句,该sql语句查询的结果会放入游标的缓冲区里面
from Student
where Sdept=:Deptname; -- where子条件是查询Deptname的系,这个Deptname是主变量,在sql里面用
-- 主变量,为表示和sql表进行区分,变量名前加冒号:
EXEC SQL OPEN SX; -- 打开游标SX,指向查询结果第一行
for( ; ; ){ // 使用for循环逐条处理结果集中的记录
EXEC SQL FETCH SX INTO :HSno,:HSname,:HSsex,:HSage;
// 推进游标,将sql中查询到的元组的数据,存放到主变量里面
if(SQLCA.SQLCODE!=0)
break; // 表示如果操作不成功,则推出for循环
if(count++==0) //如果是第一行的话,先输出行头
printf("\n%-10s %-20s %-10s %-10s\n","Sno","Sname","Ssex","Sage");
printf("\n%-10s %-20s %-10s %-10s\n",HSno,HSname,HSsex,HSage) // 打印查询结果
printf("UPDATE AGE(Y/N)?"); // 询问用户是否要更新该学生的年龄
do{
scanf("%c",&yn);
}while(yn != 'N' && yn != 'Y' && yn != 'y' && yn != 'n') //输入字符不合法
if(yn == 'y'|| yn == 'Y'){ //如果用户选择修改年龄
printf("input new age:");
scanf("%d",&NEWAGE);
EXEC SQL UPDATE Student
SET Sage=:NEWAGE
WHERE CURRENT OF SX; // 该where条件子句的意思是当前游标指向的那一行元组
}
}
EXEC SQL CLOSE SX; // 关闭游标,不再和查询结果对应
EXEC SQL COMMIT WORK; // 提交更新
EXEC SQL DISCONNECT TEST; // 断开数据库连接
8.1.3-不用游标的sql语句
- 当语句为以下种类时,可以不使用游标
- 说明性语句
- 数据定义语句DDL
- 数据控制语句DCL
- 查询结果为单记录的select语句
- 非current形式的增删改语句
-- 1. 查询结果为单记录的select语句
EXEC SQL SELECT sno,sname,ssex,sage,sdept
into :hsno,:hsname,:hssex,:hsage,:hsdept
from student
where sno=:givensno;
-- 2. 查询某个学生选修某门课程的成绩,假设我们已经把将要查询的学生的学号赋值给了主变量givensno,将课程号赋值给了主变量givencno
EXEC SQL SELECT sno,cno,grade
into :hsno,:hcno,:hgrade:gradeid -- gradeid成绩主变量上的一个指示变量
from student
where sno=:givensno and cno=:givencno;
-- 如果gradeid<0,不论Hgrade为何值,均认为该学生成绩为空值
- 说明:
- into,where,having的条件表达式都可以使用主变量
- 查询结果的列为空值,系统会自动将主变量后的指示变量设置为负值,当指示变量为负值的时候,不管主变量为何值,系统都会认为主变量为null
- 如果查询结果实际上是多条记录,程序出错,关系数据库管理系统会在SQLCA中返回错误信息
-- 1. 查询某个学生选修一号课程的成绩
EXEC SQL UPDATE SC
SET GRADE=:newgrade
where sno=:givensno;
-- 2. 某个学生新选修了某门课程,将有关记录插入sc表中,假设插入的学号已经赋值给主变量stdno,课程号已经赋值给主变量couno
gradeid=-1; //gradeid为指示变量,赋为负值
EXEC SQL INSERT
INTO SC(SNO,CNO,GRADE)
VALUES(:stdno,:couno,:gr:gradeid);
-- 由于该学生刚选修课程,成绩应该为空,所以要把指示变量设为负值
- 说明:
- 在update的set子句和where子句中可以使用主变量,set子句还可以使用指示变量
8.1.4-使用游标的SQL语句
- 必须使用游标的SQL语句如下:
- 查询结果为多条记录的select语句
- current形式的update语句
- current形式的delete语句
- 查询结果为多条记录的select语句
- 使用游标的步骤
- 说明(定义)游标
- 语句格式:exec sql declare <游标名> cursor for <select子句> 定义一个游标,是和该select子句相关联的
- 该语句是一个说明性语句,这时关系数据库管理系统不执行select语句
- 打开游标
- 语法格式:exec sql open <游标名>
- 功能:执行和游标绑定select语句,把查询结果取到缓冲区中,这是游标处于活动状态,指针指向结果集中的第一条记录
- 推进游标指针并取当前记录
- 语法格式:exec sql fetch <游标名> into <主变量1,主变量2,…> 每个主变量都可选有一个指示变量
- 功能:指定方向推动游标指针,同时将缓冲区中的记录取出来,送至主变量供主语言进一步处理
- 关闭游标
- 语法格式:exec sql close <游标名>
- 功能:关闭游标,释放结果集占用的缓冲区和其他资源
- 游标被关闭后,就不再和原来的查询结果集相联系
- 被关闭的游标可以再次被打开,与新的查询结果相联系
- current形式的update语句和delete语句
- 其实current的意思就是和游标相关
- 语法格式:update语句和delete语句中要用子句,where current of <游标名>
- 注意:如果游标定义中的select语句带有union或order by,或者定义了一个不可更新的视图,则不能使用current形式的update和delete语句
8.1.5-动态sql
- 静态嵌入式sql:可以满足一般要求,无法满足要到执行时才能够确定要提交的sql语句,查询的条件(就是说sql语句在编程序的时候,该语句结构已经固定了,可能where子句之类的会变,但是结构不会变)
- 动态嵌入式sql:在程序运行过程中可以临时组装sql语句,支持动态组装sql语句和动态参数两种形式
- 使用sql语句主变量:程序主变量包含的内容是sql语句的内容(也就是语句格式),而不是原来保存数据的输入或输出变量(这是静态sql语句主变量的作用)
exec sql begin declare section;
const char *stmt="create table test(a int);" -- sql语句主变量,内容是创建表的sql语句
exec sql end declare section;
...
exec sql execute immediate:stmt; --执行动态sql语句,结果就是在数据库里面建立了一个test表
- 动态参数:动态参数是sql语句中的可变元素,使用参数符号(?)表示该位置的数据在运行时设定,与主变量不同的是,动态参数的输入不是编译的时候完成绑定,而是通过prepare语句准备主变量和执行语句execute绑定数据或主变量来完成
- 使用动态参数的步骤:
- 声明sql语句主变量:sql语句的主变量的值包含动态参数(?)
- 准备sql语句prepare:exec sql prepare 语句名 from sql语句主变量
- 执行准备好的语句execute:exec sql execute 语句名 [into <主变量表>] [using <主变量或常量>]
先执行语句,然后,如果语句是select语句之类的,我们就需要接受结果集的值,最后使用using来给?设置值
-- 向test中插入元组
exec sql begin declare section;
const char *stmt="insert into test values(?)" -- 声明sql主变量内容是insert语句
exec sql end declare section;
...
exec sql prepare mystmt from:stmt; -- 准备语句,语句名是mystmt,其代表的就是insert into test
-- values(?)这条语句
...
exec sql execute mystmt using 100; -- 用100代替? ,所以语句变成了insert into test values(100)
exec sql execute mystmt using 200;
8.2-过程化sql
8.2.1-过程化sql的块结构
- 过程化sql是sql的扩展,增加了过程化语句功能,基本结构是块,每个块完成一个逻辑操作,块之间可以互相嵌套
- 过程化sql块的基本结构:
- 定义部分:
- declare:定义的变量,常量等只可以在该基本块中使用
- 变量,常量,游标,异常等,在基本块执行结束后,定义就不在存在
- 执行部分
begin
sql语句定义,过程化sql的流程控制语句(for语句之类的)
exception
异常处理部分
end;
9-关系查询处理和关系查询优化
9.1-关系数据库系统的查询处理
9.1.1-查询处理步骤
- 关系数据库管理系统查询处理分为:查询分析,查询检查,查询优化,查询执行.
- 查询分析:对查询语句进行扫描,词法分析和语法分析
- 词法分析:从查询语句中识别出正确的语言符号—>比如select写成了slect
- 语法分析:进行语法检查—>比如select * from test 写成了select * test
- 查询检查:
- 合法性检查:比如语句正确,但是访问的表是不存在的
- 视图转换:如果是对视图的操作,则要用视图消解方法把对视图的操作转换成对基本表的操作
- 安全性和完整性初步检查:比如检查数据字典,对用户权限进行检查
- 检查通过后把sql查询语句转换为内部表示,及等价的关系代数表达式,关系数据库管理系统一般都用查询树,也称为语法分析树来表示扩展的关系代数表达式
- 查询优化:查询优化即选择一个高效执行的查询处理策略(关系系统sql语句是一个对用户透明的语言,用户不过多关心语句的内部实现逻辑,所以我们可以进行查询优化,比如,我要求你从太原去上海,就可以优化,比如我坐飞机去,但是如果我要求你坐火车从太原到上海,这就类似于非关系系统,那么就没有优化的可能了)
- 查询优化分类
- 代数优化/逻辑优化:指关系代数表达式的优化,即按照一定的规则,通过对关系代数表达式进行等价变化,改变关系代数的次序和组合,使查询更高效,—>其实就是执行顺序的优化,看看先执行那个,后执行那个,比如说先执行投影操作效率更快,就先投影后选择之类的
- 物理优化:指存储路径和底层操作算法的选择
- 查询优化的选择依据
- 基于规则
- 基于代价
- 基于语义
- 查询执行:依据优化器得到的执行策略生成查询执行计划,由代码生成器生成执行查询计划的代码,然后执行这个查询计划,回送查询结果
- 注意:查询分析,查询检查,查询优化(只包含第一步代码优化),这三步会访问数据库数据字典
9.1.2-实现查询操作的算法示例
- 选择操作的典型实现
- 全表扫描方法:对查询的表的元组逐一扫描看是否符合要求,适合小表,不适合求最大值最小值方式
- 索引扫描方法:适合于选择条件中的属性上由索引(例如B+树索引或Hash索引),通过索引先找到满足条件的元组主码或者元组指针,再通过元组指针直接在查询的基本表中找到元组
- 连接操作的实现:连接操作是查询处理中最耗时的操作之一
- 嵌套循环算法,例如—>select * from student,sc where student.sno=sc.sno;
- 对外层循环(student)的每一个元组(s),检索内层循环(SC)中的每一个元组(sc)
- 检查这两个元组在连接(Sno)属性上是否相等
- 如果满足连接条件,就连接后输出
- 排序-合并算法:先排序,再循环
- 索引连接:
- hash join算法:把连接属性作为Hash码,用同一个hash函数把student表和sc表中的元组散列到Hash表中(算法前提:较小的表再第一阶段后可完全放入内存Hash桶中)
- 划分阶段:对包含较少元组的表进行一遍处理,把他的元组按hash函数分散到hash表的桶中
- 试探阶段/连接阶段:对包含较多元组的表(sc)进行一遍处理,把sc表的元组也按同一个hash函数进行散列,把sc元组与桶中来自student表并与之相匹配的元组连接起来
9.2-关系数据库系统的查询优化
- 在集中式数据库中,执行代价主要包括:磁盘存取块数(I/O代价是最最最最主要的,因为IO操作涉及机械动作)
- 在分布式数据库中,执行代价主要包括:总代价=I/O代价+cpu代价+内存代价+通信代价/网络代价
- 有选择和连接操作的时候,先做选择操作,这样子参加连接的元组就可以大大减少,这就是代数优化/逻辑优化
- 表的连接操作算法有全表扫描或索引扫描,当表较大的时候,采用索引连接可以大大提高效率,这就是物理优化
9.3-代数优化
- 代数优化策略是通过对关系代数表达式的等价变换来提高查询效率,其实就是改变查询语句中操作的次序和组合,不涉及底层存取路径.
- 关系代数表达式的等价是指相同的关系代替两个表达式相对应的关系,所得到的结果是相同的,两个关系表达式E1和E2是等价的
- 常见的等价变换规则:
- 连接,笛卡儿积交换律—>例如E1 X E2等价于E2 X E2
- 连接,笛卡儿积的结合律—>(E1 X E2) X E3等价于E1 X (E2 X E3)
- 投影的串接定律
- 选择的串接定律,比如先按条件F1,在按条件F2选择,那么会对全表进行两次扫描,我们可以直接对表进行一次F1 AND F2的扫描,这样子只对全表扫描了一次
- 选择与投影操作的交换律:尽量让选择操作先做
- 选择与笛卡儿积的交换律:尽量先选择操作
- 选择与并的分配律
- 选择与差运算的分配律
- 选择对自然连接的分配律
- 投影与笛卡儿积的分配律
- 投影与并的分配律
- 典型的启发式规则
- 选择运算尽可能先做
- 把投影运算和选择运算同时进行
- 把投影同前面的双目运算结合起来
- 找出公共的子表达式
9.4-物理优化
- 就是要选择高效合理的操作算法或存取路径,求德优化的查询计划
- 选择操作的启发式规则
- 小关系:全表顺序扫描,哪怕有索引也不用
- 大关系:
- 若选择条件是主码,用索引,表一般会自动在主码上加索引
- 若选择条件是非主属性,若该列有索引,则估算查询结果的元组数目,若符合要求的数据占所有数据的比例较小,就使用索引,否则还是全表扫描
- 对于and连接的合取选择条件:如果条件属性上有索引,则优先索引
- 对于or,一般使用全表顺序扫描
- 连接操作的启发式规则:
- 如果两个表按照连接属性排序了:排序-合并算法
- 如果一个表在连接属性上有索引:索引连接算法
- 若以上都不适用,其中一个表较小,则hash join算法
- 可以用嵌套循环方法,并且小表作为外表
10-数据库恢复技术
事务
- 事务:是用户定义的一个数据库操作系列,这些操作要么全做,要么全不做,是一个不可分割的工作单位,是恢复和并发控制的基本单位,事务是数据库的逻辑工作单位
- 事务和程序的区别:在关系数据库中,一个事务可以是一条或一组sql语句或者整个程序,一个程序通常包含多个事务
- 事务的定义:
- 显式定义方式:事务正常结束,提交事务的所有操作(读+更新),即事务中所有对数据库的更新写回到磁盘上的物理数据库中,若事务不可以正常结束,则会回滚到未执行事务前的状态
-- 事务正常结束格式
begin transaction;
sql语句;
commit;
-- 事务异常终止格式
begin transaction;
sql语句;
rollback;
- 事务的特性:
- 原子性:操作要么全做,要么全不做
- 一致性:事务执行的结果是使数据库从一个一致性状态变到另一个一致性状态(两个用户读取同一个数据结果是一样的)
- 隔离性:一个事务的执行不可以被其他事务干扰
- 持续性:一个事务一旦提交,数据库中的数据就是永久改变的了,接下来的操作或故障不应该对其执行结果有任何改变
数据库恢复的作用
- 数据库恢复的作用: 把数据库从错误状态恢复到某一个已知的正确状态,是数据库的最后一道防线,对系统的可靠程度起决定性的作用 ,恢复子系统的代码占整个系统的10%
- 故障的分类:
- 事务内部的故障:有的是预期的,有的是非预期的
- 事务撤销:一种恢复操作,撤销该事务已经作出的任何对数据库的修改,使得该事务好像没有启动一样
-- 银行转账事务,这个事务是把一笔金额从一个账户甲转给另一个账户乙
begin transaction
读账户甲的金额balance;
balance=balance-amount; --amount为转账金额
if(balance<0) then
{
打印'金额不足,不能转账'; -- 事务内部可能造成事务回滚的情况
rollback; -- 事务回滚,撤销已做的修改,数据库恢复的正确的状态,这就是可预期的错误
-- 但是事务内部更多的故障是非预期的,比如运算溢出,死锁等,一般我们说的故障,指的都是非预期故障
}
else
{
读账户乙的余额balance1;
banlance1=balance1+amount;
写会balance1;
commit;
}
- 系统故障(软故障):指造成系统停止运转的任何事件,使得系统要重新启动
- 故障表现:内存缓冲区中的信息全部丢失,所有正在运行的事务都非正常终止,不破坏数据库
- 常见原因:cpu温度过高,系统断电等
- 介质故障(硬故障):外存故障,如磁盘损坏,磁头碰撞
- 故障表现:破坏数据库,介质故障发生几率小,但是破坏性很大
- 计算机病毒:可繁殖,传播,并且对计算机造成破坏的计算机程序
- 特点:隐蔽性,潜伏性,传染性,破坏性,寄生性
- 恢复的实现技术:恢复机制涉及的关键问题是如何建立冗余数据,如何利用这些冗余数据实施数据库恢复
- 建立数据冗余的常见方法:数据转储(backup)和登记日志文件(logging)
- 数据转储:指数据库管理员定期地将整个数据库复制到其他介质上(例如磁盘)保存起来的过程,这些备用的副本称作后备副本或后援副本–>该副本只负责把数据库恢复到转储时的状态,如果想恢复到故障前,则需要日志文件的帮助
- 转储状态分为静态存储和动态存储
- 静态转储:在系统中无事务时进行的转储操作,即转储期间不允许对数据库进行任何存取,修改活动
- 优点:实现简单
- 缺点:降低了数据库的可用性,因为转储必须等待正在运行的用户事务结束,新的事务必须等转储结束
- 动态转储:转储操作与用户事务并发进行,转储期间允许对数据库进行存取或修改
- 优点:不用等待正在运行的用户事务结束,不会影响事务的运行
- 缺点:不能保证副本中的数据正确有效,比如我们正转储呢,同时还修改着数据,那么我们转储的数据有部分就是过时的
- 所以,动态转储期间,要把各事务对数据库的修改活动登记下来,建立日志文件,后备副本加上日志文件就可以把数据库恢复到某一时刻的正确状态
- 转储需要的数据量:转储方式—>海量转储与增量转储
- 海量转储:每次转储全部数据库
- 增量转储:只转储上次转储后更新过的数据
- 比较:从恢复角度看,使用海量转储得到的后备副本进行恢复更方便,如果数据库很大,事务处理又很麻烦,则增量转储方式更实用更有效
- 转储方式又两种,转储状态又两种,结合,所以一共有四种数据转储方式–>动态海量转储,静态海量转储…
- 登记日志文件:日志文件是用来记录事务对数据库的更新操作
- 日志文件的格式(2种)
- 以记录为单位的日志文件,需要记录事务的开始和结束标记,事务的所有更新操作
- 以数据块为单位的日志文件
- 日志文件的作用:用于事务故障恢复和系统故障恢复,并协助后备副本进行介质恢复,具体为
- 事务故障恢复和系统故障恢复必须建立日志文件
- 在动态转储方式中必须建立日志文件,后备副本和日志文件结合起来才能有效的恢复数据库
- 在静态转储方式中,也可以建立日志文件
- 登记日志文件必须遵循的原则
- 登记的次序严格按并发事务执行的时间次序
- 必须先写日志文件,后写数据库
- 恢复策略
- 恢复方法:系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
- undo(撤销)故障发生时未完成的事务
- redo(重做)已完成的事务
- 系统故障恢复步骤:
- 正向扫描日志文件(即从头到尾扫描日志文件):找出故障前已经提交的事务,加入redo重做队列,找出故障发生时未完成的事务,加入undo撤销队列
- 对撤销队列事务进行撤销处理–>反向扫描日志文件,对每个撤销事务的更新操作执行逆操作,即将日志记录中更新前的值写入数据库
- 对重做队列事务进行重做处理–>正向扫描日志文件,对每个重做事务重新执行登记的操作,即将日志记录中更新后的值写入数据库
- 介质故障的恢复:恢复方法时重装数据库,然后重做已完成的事务
- 装入最新的后备数据库副本,使数据库恢复到最近一次转储时的一致性状态
- 对于静态转储, 装入后数据库就已经处于一致性状态
- 对于动态转储, 还须同时装入转储日志文件副本,利用恢复系统故障的方法(redo+undo),使得数据库恢复到一致性状态
- 装入有关的日志文件副本(转储结束时刻的日志文件副本),重做redo已完成的事务
- 说明:介质故障的恢复需要数据库管理员介入,数据库管理员的工作是重装最近转储的数据库副本和有关的各日志文件副本.
- 具有检查点的恢复技术
- 日志技术有两个问题:一个是搜索整个日志文件将耗费大量的时间,二是重做处理,重新执行,浪费了大量的时间,所以,我们提出了检查点技术以解决上述问题,在日志文件中增加检查点记录(该记录包含事务以及事务开始的地址),增加重新开始文件(该文件中记录了检查点记录的地址),恢复子系统在登陆文件期间动态的维护日志,注意,日志文件和重新开始文件是并列的,我们是在又日志文件的基础上,再创建一个重新开始文件,而不是再日志文件里创建重新开始文件
- 检查点记录的内容:
- 建立检查点时刻,所有正在执行的事务清单
- 这些事务最近一个日志记录的地址
- 重新开始文件的内容
- 记录各个检查点记录在日志文件中的地址
- 过程
- 重新开始文件中主要记录的是检查点记录的地址
- 比如我们要调取c检查点之后执行的事务,我们就先查找重新开始文件,从里面,找到c检查点在日志文件中的地址
- 找到地址后,我们加入日志文件查找,找到检查点,而检查点记录的就是该检查点生成的时候,正在执行的事务地址
- 然后我们就可以很快的扫描该记录点找到各个记录的地址,而不用扫描整个日志文件
- 动态维护日志文件的方法:
- 数据库镜像(针对介质故障)
- 介质故障是对系统影响最为严重的一种故障,为了有效的预防介质故障,提高数据库可用性,提出了数据库镜像的解决方案
- 数据库镜像:数据库管理系统会自动的把整个数据库或其中的关键数据复制到另一个磁盘上,每当主数据库更新时,数据库管理系统自动把更新后的数据复制过去,保证镜像数据与主数据的一致性
- 出现介质故障后,数据库镜像提供服务,同时数据库管理系统尽快利用数据库镜像恢复数据库,不需要关闭和重装数据库副本
- 没有故障的时候,数据库镜像可以用于并发操作,进行负载均衡
11-并发控制
- 多用户操作系统是允许多个用户同时使用的操作系统,其特点是同一时刻并发运行的事务数可达成百上千个,多用户数据库系统涉及并发操作,例如:飞机订票数据库系统,银行数据库系统
- 多事务执行方式:
- 事务串行执行:每个时刻只有一个事务运行,其他事务必须等到这个事务结束后才可以运行,不可以充分利用资源,发挥数据库共享资源的特点.
- 交叉并发(单处理机系统):
- 同时并发(多处理机系统):多个处理机可以运行多个事务,真正的实现多个事务并行运行
11.1-并发控制概述
- 事务是并发控制的基本单位,并发控制的任务是:
- 对并发操作进行正确调度
- 保证事务的隔离性
- 保证数据库一致性(不一致性包括:丢失修改,不可重复读,读脏数据)
- 读脏数据:读取到的数据和数据库内的数据不一致
- 不可重复读:两次一样的操作,因为中间有别的事务修改了数据,导致相同操作,结果却不同
- 丢失修改:两个事务,先后修改数据,后者的修改导致前者的修改白修改了,也就是修改丢失
- 并发控制:就是要用正确的方式调度并发操作,使得一个用户事务的执行不受其他事务的干扰,从而避免数据的不一致性
- 并发控制的主要技术
- 封锁
- 时间戳timestamp
- 乐观控制法
- 并发控制
- 说明两个符号R(x)代表读数据x,W(x)表示写数据x
11.2-封锁
- 封锁的概念:封锁就是事务t在对某个数据对象(例如表,记录)操作之前,先向系统发出请求,对其加锁.加锁后事务t就对该事务有了一定的控制,在事务t释放他的锁之前,其他的事务不能更新(有时候是允许读的)此数据对象
- 基本封锁类型:前面说了,加锁后拥有的是一定的控制,一个事务对某个数据对象加锁后究竟拥有什么样的控制由封锁的类型决定
- 排他锁(exclusive locks—>X锁):又称为写锁
- 若事务T对数据对象加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,知道T释放A上的锁,保证其他事务在T释放A上的锁之前不能再读取和修改A
- 共享锁(share locks—>S锁):又称为读锁
- 若事务T对数据对象A加上S锁,则视图T可以读A但是不可以修改A,其他事务只可以对对象A加S锁,而不能加X锁,直到T释放A上的S锁,保证其他事务可以读A,但在T释放A上的S锁之前不可以对A做任何修改
11.3-封锁协议
- 封锁协议是指在运用X锁和S锁对数据对象加锁的时候,需要约定一些规则,这些规则为封锁协议,例如何时申请X锁或S锁,持锁时间,何时释放等
- 三级封锁协议:三级协议的主要区别是什么操作需要申请封锁,以及何时释放锁(即锁的持续时间)
- 一级封锁协议是指事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放,事务结束包括正常结束和非正常结束
- 一级封锁协议作用
- 可以防止丢失修改,并保证事务T可恢复
- 在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读脏数据
- 二级封锁协议是在一级封锁协议上,加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁
- 二级封锁协议作用:
- 二级封锁协议可以防止丢失修改和读脏数据
- 在二级封锁协议中,由于读完数据R后即可释放S锁(没有坚持到事务结束再释放),所以他不能保证重复读
- 三级封锁协议是指在一级封锁协议上,加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放(和二级封锁协议的根本性区别就是,二级是读取后就释放S锁,而三级是事务结束后才释放)
- 三级封锁协议作用:三级封锁协议可以防止丢失修改,不可重复读,读脏数据
11.4-活锁和死锁
- 活锁:t1封锁了数据R,t2就要等待,然后t3也等待,t1完成后,系统首先批准了t3的请求,之后批准t4的请求,t2永远等待,这就是活锁的情形.
- 避免活锁:采用先来先服务策略—>等待时间越长,获得的概率越大
- 死锁:活锁是长时间等待,但是一定会获得数据,但是死锁就是永远都无法获得数据
- 解决死锁的的方法:死锁的预防和死锁的诊断与解除
- 死锁的预防(在操作系统中有时候可行,但是在数据库中,效率很低,无法实施):预防死锁的发生就是要破坏死锁的发生条件,有一次封锁法和顺序封锁法
- 一次封锁法:要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行
- 存在的问题:降低系统并发度,难以事先精确确定封锁对象
- 顺序封锁法:预先对数据对象规定一个封锁顺序,所有的事务都按这个顺序实行封锁
- 存在的问题:维护成本高,难以实现
- 死锁的诊断和解除(在数据库一般使用这个方式)
- 超时法:如果事务的等待时间超过了规定的时限,就认为发生了死锁
- 优点:实现简单
- 缺点:一是有可能误判死锁,二是若把时限设置的太长,死锁发生后不能即使发现
- 等待图法:只要有回路,就代表发生了死锁
- 说明:
- 并发控制子系统周期性生成事务等待图,检测事务,如果发现图中存在回路,则代表系统中出现了死锁
- 解除死锁的方法是选择一个处理死锁代价最小的事务,将其撤销,释放此事务持有的全部锁,使得其他的事务可以继续执行下去
11.5-并发调度的可串行性
- 数据库管理系统对并发事务不同的调度会产生不同的结果,什么样的调度是正确的呢?就要把并发调度的事务运行结果和这些事务串行调度后的结果进行比对,若相等,则该调度是正确的,称为可串行化调度
- 可串行性:是并发事务正确调度的准则,按照这个准则一个给定的并发调度,当且仅当他是可串行化的,才认为是正确调度---->只要是串行的,就是标准答案,标准答案不唯一,只要并发调度的结果符合标准答案其中一个,就说这个并发调度是可串行的
- 冲突操作:是指不同事务对同一数据的读写操作和写写操作
- 冲突可串行化:一个调度Sc在保证冲突操作次序不变的情况下,通过交换两个事务不冲突操作的操作的顺序,得到另一个调度S,如果S是串行的,称Sc是冲突可串行化的调度
- 冲突可串行化调度可串行化调度的充分条件,但不是必要条件
11.6-两段锁协议
- 两段锁协议(为了保证串行事务的正确性):指所有事务必须分为两个阶段对数据项加锁和解锁,即在对任何数据进行读,写操作之前,事务首先要获得对数据的封锁,在释放一个封锁之后,事务不在申请和获得任何其他的封锁,数据库管理系统普遍使用两段锁的方法实现并发调度的可串行性,从而保证调度的正确性.
- 两段锁含义:事务分为两个阶段
- 第一阶段是获得封锁,也称为扩展阶段,事务可以申请任何数据项上的任何类型的锁,但是不能释放任何锁
- 第二阶段是释放封锁,也成为收缩阶段,事务可以释放任何数据项上的任何类型的锁,但是不能再申请任何锁
- 就是不能加锁解锁加锁解锁混起来,要一次性加完锁,然后一次性解完锁
- 遵守两段锁协议的,一定是可串行化调度
- 说明:
- 事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件
- 若并发事务都遵守两段锁协议,则对这些事务的任何并发调度策略都是可串行化的
- 若并发事务的一个调度是可串行化的,不一定所有事务都符合两段锁协议
- 两段锁协议和预防死锁的一次封锁法的异同:
- 一次封锁法要求一次性将所有需要的数据都上锁,这个方式最大的缺点就是难以预见,我们很难一次性就想好哪些数据是需要的
- 但是两段锁协议改善了这一点,他要求不那么严格,当你需要某一数据时,加上就可以了,但是不允许之前有解锁操作
11.7-封锁的粒度
- 封锁对象的大小称为封锁力度
- 封锁的对象:逻辑单元,物理单元
- 逻辑单元:属性值,属性值的集合,元组,关系等等
- 物理单元:页(数据页或索引页),物理记录等
- 说明:
- 封锁粒度与系统的并发度和并发控制的开销密切相关(比如我们的封锁粒度设置为一个表,那么我们一次封锁一个表,虽然我们只对学号修改,但是别人此时也无法修改姓名和年龄,并发度就较低)
- 封锁的粒度越大,数据库所能够封锁的数据单元就少,并发度就越小,系统开销也越小
- 封锁的粒度越小,并发度较高,但系统开销较大
- 多粒度封锁是指:在一个系统中同时支持多种封锁粒度供不同的事务选择
- 选择封锁粒度的原则:考虑封锁开销和并发度
- 处理多个关系的大量元组的的用户事务:以数据库为封锁单位
- 处理大量元组的用户事务:以关系为封锁单位
- 只处理少了元组的用户事务:以元组为封锁单位
- 多粒度树:以树形结构来表示多级封锁粒度,根节点是整个数据库,表示最大的数据粒度,叶节点表示最小的数据粒度
- 多粒度封锁协议:允许多粒度树中的每个节点被独立的加锁,对一个节点加锁意味着对该节点所有后裔节点加锁(例如对student加锁,则对该表的每一个元组都加了所),在多粒度封锁中,一个数据对象可能以两种方式封锁,两种封锁方式效果一样
- 显示封锁:直接加到数据对象上的封锁
- 隐式封锁:虽然没有对该数据对象没有独立加锁,但是由于其上级节点加锁而使得该节点也加上了锁
- 意向锁
- 引进意向锁的目的:提高对某个数据对象加锁时系统的检查效率(检查效率就是说,按照之前的额加锁方式,我们加一个锁,我们还需要看看之祖先的锁和后裔的锁,效率很低)
- 意向锁的含义:如果对一个节点加意向锁,则说明该节点的下层节点正在被加锁,对任一节点加基本锁,必须先对他的上层节点加意向锁,所以意向锁的作用就是告诉系统,下级节点已经有节点加锁了
- 常用意向锁:
- 意向共享锁(IS–>intent share lock):如果对一个数据对象加is锁,表示他的后裔节点拟加S锁
- 例如:事务t要对R中某个元组加S锁,则要现对关系R和数据库加IS锁
- 意向排他锁(IX–>exclusive):如果对一个数据对象加IX锁,表示他的后裔节点拟加X锁
- 共享意向排他锁(SIX):如果对一个数据对象加SIX锁,表示对他加S锁,再加IX锁
- 例如:对student表加了six锁,表示先对student表加一个s锁,然后对其的个别元组加一个X锁,然后因为元组家里X锁,则需要对student表加一个IX锁,所以studet表合起来有两个锁:s,ix,所以就是SIX锁
- 说明:
- 如果一个对象加了X锁,则其不可以再加任何锁
- 锁的强度:是指它对其他锁的排斥程度,一个事务再申请封锁时以强锁代替弱锁是安全的,反之则不然
- X>SIX>S=IX>IS
- 具有意向锁的多粒度封锁方法:任何事务要对一个数据对象加锁,申请封锁的时候应该按自上而下的次序进行,释放封锁时则应该按自下而上的次序进行
- 例如:事务t1要对关系R加S锁
- 对数据库加is锁
- 检查数据库和R1是否已经加了不相容的锁(x或ix)
- 不再需要搜索和检查R1中的元组是否加了不相容的锁
小结
- 数据库的并发控制以事务为单位,通常使用封锁机制
- 并发控制机制:并发控制机制调查事务正确性的判断准则时可串行性
- 并发操作的正确性通常用两段锁协议来保证