在终端交互方式下使用,称为交互式SQL
嵌入在高级语言的程序中使用,称为嵌入式SQL
高级语言如C、Java等,称为宿主语言
嵌入式SQL的实现方式
名称 | 全称 | 简称 | 特点或功能或构成或职责 |
---|---|---|---|
数据库 | 存储数据的仓库,数据有组织的进行存储 | DataBase (DB) | 1.共享 2.冗余度小 3.数据独立性高 4.易扩展 5.按照一定的数据模型组织,描述和储存 |
数据库管理系统 | 操纵和管理数据库的大型软件 ,是位于用户和操作系统之间的一层数据管理软件 | DataBase Management System(DBMS) | 1.数据定义 2.数据操纵 3.数据库的运行管理 4.数据库的建立和维护功能 |
数据库系统 | 是指在计算机系统中引入数据库后的系统构成 | Database System(DBS) | 1.数据库 2.数据库管理系统 3.应用系统(及其开发工具) 4.相关人员(数据库管理人员、应用系统开发人员、用户) |
SQL | 操纵关系型数据的编程语言,定义了一套操作关系型数据库统一标准 | Structured Query Language(SQL) | ⒈ 综合统一 2. 高度非过程化 3.面向集合的操作方式 4. 同一种语法结构提供两种使用方式(自含式语言和嵌入式语言) 5. 语言简捷,易学易用(3大类,11个命令词) |
数据库管理员 | 负责全面管理和控制数据库系统,是数据库系统中最重要的人员 | DatabaseAdministrator(DBA) | 1、设计和定义数据库系统 2、帮助最终用户使用数据库系统 3、监督和控制数据库系统的使用和运行 4、改进和重组数据库系统 5、调优数据库系统的性能 6、转储和恢复数据库 7、重构数据库 |
主流的关系型数据库管理系统有: ORACLE、MYSQL、Microsoft SQL Serever等
关系型数据库
概念:建立在关系模型的基础上,有多张相互连接的二维表组成的数据库
特点:
1、使用表存储数据,格式统一,便于维护
2、使用SQL语言操作,标准统一,使用方便
应用程序与数据之间相互独立
定义:指用户的应用程序与数据库的逻辑结构是相互独立的。数据的逻辑结构改变了,应用程序也可以不变
外模式/模式映像保证了数据的逻辑独立性:
定义:指用户的应用程序与存储在磁盘上的数据库
中数据是相互独立的。当数据的物理存储改
变了,应用程序不用改变。
模式/内模式映象的用途是保证数据的物理独立性:
定义: 在数据库中用数据模型这个工具来抽象、表示和处理现实世界中的数据和信息。
数据模型就是现实世界的模拟
①网状模型 ② 层次模型 ③XLM ④JSON
其中①②是早期的,③④是两种经常在网络使用的数据表示格式
数据模型的根本区别在于数据结构不同
数据结构: 表
定义: XML是可扩展标记语言(eXtensible Markup Language)的缩写,常用于传输和存储数据
它是一种数据表示格式,可以描述非常复杂的数据结构
特点:
一是纯文本,默认使用UTF-8编码。
二是可嵌套,适合表示结构化数据。
如果把XML内容存为文件,那么它就是一个XML文件,例如book.xml。
此外,XML内容经常通过网络作为消息传输。
XML的特点是功能全面,但标签繁琐,格式复杂。
在Web上使用XML现在越来越少,取而代之的是JSON这种数据结构
JSON是JavaScript Object Notation的缩写,它去除了所有JavaScript执行代码只保留JavaScript的对象格式
单一的数据结构: —— 关系
–现实世界的实体及实体间的各种联系均用关系表示
数据的逻辑结构: —— 二维表
–从用户的角度出发,关系模型中数据的逻辑结构是一张二维表
关系模型建立在集合代数的基础上
域: 是一组具有相同数据类型的值的集合
笛卡尔积: D1×D2×… ×Dn= {(d1, d2, … , dn)|di∈Di, i=1, 2, … , n}
关系: D1×D2×… ×Dn的子集叫作在域D1, D2, … , Dn上的关系
关系也是一个二维表,表的每行对应一个元组,表的每列对应一个域
码:由一个或多个属性组成
超码:能够推出所有属性的属性组的集合,候选码是极小的超码集,是超码的子集
候选码(Candidate Key):在关系中能唯一标识元组的属性或属性集
主属性(Prime Attribute):候选码的各个属性
非主属性:不包含任何一个候选码中的属性
主码(Primary Key):也称实体标识符和主键.当有多个候选码时,挑出一个作为主码
全码(All-key):关系的所有属性是这个关系的候选码
三类关系:
基本关系(基本表或基表): 实际存在的表,是实际存储数据的逻辑表示
查询表: 查询结果对应的表
视图:由基本表或其他视图导出的表,是虚表,不对应实际存储的数据
关系模式: 是对关系的描述,关系模式是型,关系是值。如:
关系: – 学生
关系模式: – 学生(学号,姓名 ,年龄,性别,籍贯)
关系模式通常可以记为 R (U) 或 R (A1,A2,…,An) R 关系名 ; A1,A2,…,An 属性名
关系模式VS关系:
关系模式: 对关系的描述,是静态的、稳定的.
关系: 关系模式在某一时刻的状态或内容,是动态的、随时间不断变化的.
关系数据库: 在一个给定的应用领域中,所有实体及实体之间联系的关系的集合构成一个关系数据库
例如:教学管理数据库中有四个关系:
教师关系T,课程关系C,学生关系S,选课关系SC
关系数据库模式是关系数据库的型,是对关系数据库的描述
1. 实体完整性
主码唯一且不能为空值
主属性不能取空值
2. 参照完整性
外码: 如果一个关系R中的一个属性F对应着另一关系S的主码K,那么F在关系R中称为外码
说明:
①关系R和S不一定是不同的关系
学生(学号,姓名,性别,专业号,出生日期,班长)
②被参照关系S的主码K和参照关系的外码F必须定义在同一个(或一组)域上
③外码并不一定要与相应的主码同名
④当外码与相应的主码属于不同关系时,往往取相同的名字,以便于识别
参照完整规则: 定义外码和主码之间的引用规则
外码的取值必须为:
①或取空值
②或等于被参照表中某个元组的主码值
3. 用户定义的完整性
用户定义的完整性: 是针对某一具体关系数据库的约束条件,反映某一具体应用所涉及的数据必须满足的语义要求。
关系模型 应提供定义和检验这类完整性的机制,以便用统一的系统的方法处理它们,而不要由应用程序承担这一功能
当DBMS处理SQL查询时,首先是将其转换成关系代数或相似的内部表现形式
关系代数定义: 是一张抽象的数据查询语言,是对关系的运算来表达查询
运算对象: 关系
运算符: 四类
运算结果: 关系
R 运算符 S
注意:
① R 和S 的属性个数必须相同
② 对应属性必须取自同一个域
投影:
用于从R中选择出若干属性列组成新的关系
选择:
用于在关系R中选择满足给定条件的各个元组
σc( R ):
– C:选择条件,是一个逻辑表达式
– 结果为只包含R中某些元组的新的关系
一般连接与自然连接的不同
等值连接与自然连接的不同
① 等值连接不要求相等属性值的属性名相同,而自然连接要求相等属性值的属性名必须相同,即量关系只有在同名属性才能进行自然连接
② 等值连接不将重复的属性值去掉,而自然连接去掉重复属性,也就是说,自然连接是去掉重复列的等值连接
等值连接与自然连接的相同
都是根据属性值相等进行连接
设有关系R、S 如图所示,求R÷S 的结果
求解步骤:
①找出关系R和关系S中相同的属性,即Y属性。在关系S中对Y做投影(即将Y列取出);所得结果如下
②被除关系R中与S中不相同的属性列是X ,关系R在属性(X)上做取消重复值的投影为{X1,X2};
③求关系R中X属性对应的像集Y
④判断包含关系。
R÷S其实就是判断关系R中X各个值的像集Y是否包含关系S中属性Y的所有值。对比即可发现:
X1的像集只有Y1,不能包含关系S中属性Y的所有值,所以排除掉X1;
而X2的像集包含了关系S中属性Y的所有值,所以R÷S的最终结果就是X2
关系代数的表达式树(Expression Tree)
复杂的代数表达式
两个投影到名字上面的表不能直接自然连接,因为没没有相同的属性,应该将他们分别重命名再连接
13.查询选修了全部课程的学生的学号和姓名;
(πSno,Cno (SC) ÷π Cno (SC))∞π Sno,Sname (Students)14.查询选修了学号为"98001"学生所学过的所有课程的学生学号和姓名.
(πSno,Cno (SC) ÷π Cno (☌ Sno =‘98001’(SC)) )∞π Sno,Sname (Students)
net start mysql80
net stop mysql80
mysql [-h 127.0.0.1] [-P 3306] -u root -p
两个[],前者指定连接的是哪个IP,后者指定连接的是哪个端口,当省略时候,默认连接本机,即 mysql -u root -p
MySQL\MySQL server 8.0\bin\
的地址,添加到path中,默认是在C盘中1、SQL语句以分号结尾
2、MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
3、注释:
单行注释: - - 注释内容或 # 注释内容(MySQL特有)注意:- -与注释内容之前有空格
多行注释: /*注释内容*/
分类 | 全称 | 说明 | 动词 |
---|---|---|---|
DDL | Data Definition Lauguage | 数据定义语言,用来定义数据对象(数据库,表,字段) | CREAT DROP ALTER |
DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增改删 | SELECT INSERT UPDATE DELETE |
DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 | |
DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 | GRANT REVOKE |
总结:
SHOW DATABASE;
SELECT BATABASE();
注意:在查询当前数据库之前,还要使用一个语句:
USE 数据库名;
每个语句后面记得加分号;
创建
CREATE DATABASE [IF NOT EXISTS] 数据库名 [DEAFULT CHARSET 字符集] [COLLATE 排序规则]
复制
create table 表名 like 基表;
删除
DROP DATABASE [IF EXIST] 数据库名;
上图中:information_schema、mysql、performance_schema、sys 为系统自带的数据库
1、 查询当前数据库的所有表
SHOW TABLES;
2、查询表结构
DESC 表名;
3、查询指定表的建表语句
SHOW CREATE TABLE表名;
CREAT TABLE 表名{
字段1 类型[comment 字段1注释][列级完整性约束条件],
字段2 类型[comment 字段2注释][列级完整性约束条件],
......
字段n 类型[comment 字段n注释][列级完整性约束条件]
注意:最后一个字段的末尾不加逗号
}[comment 表的注释] [表级完整性约束条件][;]
– <列级完整性约束条件>:涉及相应属性列的完整性约束条件
– <表级完整性约束条件>:涉及一个或多个属性列的完整性约束条件
--表级完整性约束与列级完整性约束
常用完整性约束
– 主码约束: PRIMARY KEY
– 参照完整性约束: FOREIGN KEY…REFERENCES…
– 唯一性约束:UNIQUE
– 非空值约束:NOT NULL
– 取值约束:CHECK
--SQL支持的数据类型
- 第一大类:整数数据
-– bigint: 以8个字节来存储正负数, 范围:-26^3到 2^63-1
-– int: 以4个字节来存储正负数,范围:-2^31到2^32-1
–-smallint: 以2个字节来存储正负数.,范围:-2^15到2^15-1
–-tinyint: 是最小的整数类型,存储正整数,仅用1字节,范围:0至2^8-1
-– bit: 值只能是0或1,当输入0以外的其他值时,系统均认为是1
常用来表示真假、男女等二值选择
- 第二大类:精确数值数据
-– decimal:用来存储从-10^38+1到10^38-1的固定精度和范围的数值型数据
• 必须指定范围和精度:decimal (p[,q]) 例:decimal (10,2)
–-numeric:和decimal相同
- 第三大类:浮点数值数据
-– float: 用8个字节来存储数据.最多可为53位,范围为:-1.79E+308至1.79E+308.
-–real: 位数为24,用4个字节,数字范围:-3.04E+38至3.04E+38
- 第四大类:字符串数据
- – char: char(n)固定的长度为 n个字符的字符串, 不足的长度会用空格补上
- – varchar: varchar(n)可变的最长长度为n个字符的字符串,尾部的空格会去掉.
- 第五大类:日期时间数据
-– date: 日期类型
• DATE 'yyyy-mm-dd'
• Example: DATE '2004-09-30'
-– time:时间类型
• TIME 'hh:mm:ss'
• Example: TIME '15:30:02.5‘
-–datetime:日期时间类型
数据类型:
主要分三类:
1、 数值类型
CREAT TABLE emp{
id INT COMMENT'编号',
workno VARCHAR(10) COMMENT '工号',
name VARCHAR(10) COMMENT'姓名',
gender CHAR(1) COMMENT'性别',
age TINYINT UNSIGNED COMMENT'年龄',
IDcard CHAR(18) COMMENT'身份证号',
entrydata DATA COMMENT'入职时间'
}COMMENT '员工表';
1、添加字段(ADD)
ALTER TABLE 表名 ADD 字段名 类型(长度)[COMMENT 注释] [约束];
2、修改(ALTER)
ALTER TABLE 表名 ADD 字段名 新数据类型(长度);
ALTER TABLE 表名 CHANGE/MODIFY 旧字段名 新字段名 类型(长度)[COMMENT注释][约束];
ALTER TABLE 表名 RENAME TO 新表名;
ALTER TABLE Students ADD UNIQUE(Sname);
◆删除学生姓名必须取唯一值的约束。
ALTER TABLE Student DROP UNIQUE(Sname);
◆删除主键约束
ALTER TABLE Student DROP 主键约束的名字;
3、删除(DROP)
ALTER TABLE 表名 DROP 字段名;
视图定义的SELECT语句: 不能包含ORDER BY子句和DISTINCT短语
DROP TABLE[IF EXISTS]表名;
◆ 系统从字典中删去:
• 该基本表的描述
• 该基本表上的所有索引的描述
• 该基本表表中的数据
◆表上的视图往往仍然保留,但无法引用
TRUNCATE TABLE 表名
视图(VIEW)的特点:
①虚表,是从一个或几个基本表(或视图)导出的表
②只存放视图的定义,不会出现数据冗余
③基表中的数据发生变化,从视图中查询出的数据也随之改变
基于视图的操作:
- 定义视图(DDL)
-建立
-定义基于该视图的新视图
-删除- 查询视图(DML)
- 更新视图(DML)
CREATE VIEW 视图名 [列名,列名...列名] AS <子查询> [WITH CHECK OPTION];
注意:
① 视图定义的SELECT语句:不能包含ORDER BY子句和DISTINCT短语
② WITH CHECK OPTION:透过视图进行增删改操作时,不得破坏视图定义中的谓词条件(即子查询中的条件表达式)
③ DBMS执行CREATE VIEW语句时只是把视图的定义存入数据字典,并不执行其中的SELECT语句。
只是在对视图查询时,才按视图的定义从基本表中将数据查出
④ 列名可能是已经重新命名后的
常见的视图形式
①行列子集视图
②WITH CHECK OPTION的视图
③基于多个基表的视图
④基于视图的视图
⑤带表达式的视图
⑥分组视图
DROP VIEW <视图名>;
注意
:
① 该语句从数据字典中删除指定的视图定义
② 由该视图导出的其他视图定义仍在数据字典中,但已不能使用,必须显式删除
③ 删除基表时,由该基表导出的所有视图定义都必须显式删除
从用户角度而言,查询视图与查询基本表的方法相同
DBMS实现视图查询的方法:
——① 视图实体化法(View Materialization)
• 进行有效性检查,检查所查询的视图是否存在。如果存在,则从数据字典中取出视图的定义
• 执行视图定义,将视图临时实体化,生成临时表
• 将查询视图转换为查询临时表
• 查询完毕删除被实体化的视图(临时表)
——② 视图消解法(View Resolution)
• 进行有效性检查,检查查询的表、视图等是否存在。如果存在,则从数据字典中取出视图的定义
• 把视图定义中的子查询与用户的查询结合起来,转换成等价的对基本表的查询
• 执行修正后的查询
例:
视图消解法的局限:
——有些情况下,视图消解法不能生成正确查询。
——采用视图消解法的DBMS会限制这类查询。
从用户角度而言,更新视图与更新基本表的方法相同,
DBMS实现视图更新的方法:
——① 视图实体化法(View Materialization)
——② 视图消解法(View Resolution)
- 行列子集视图是可更新的。
- 除行列子集视图外,还有些视图理论上是可更新的,但它们的确切特征还是尚待研究的课题。
- 还有些视图从理论上是不可更新的
不可更新的视图与不允许更新:
- 不可更新的视图与不允许更新的视图是两个不同的概念
- 实际系统对视图更新的限制:① 允许对行列子集视图进行更新 ②对其他类型视图的更新不同系统有不同限制
视图的作用:
– 1. 视图能够简化用户的操作
– 2. 视图使用户能以多种角度看待同一数据
– 3. 视图对重构数据库提供了一定程度的逻辑独立性
– 4. 视图能够对机密数据提供安全保护
①在一个基本表上最多只能建立一个聚簇索引
②在最常查询的列上建立聚簇索引以提高查询效率
③对于经常更新的列不宜建立聚簇索引
例子:
column—字段
values—值
1. 给指定字段添加数据
INSERT INTO 表名 (column1, column2, column3....)
VALUES (value1, value2, value3.....);
2. 给全部字段添加数据
INSERT INTO 表名
VALUES (value1, value2, value3....);
3. 批量添加数据
INSERT INTO 表名 (column1, column2, column3....),(column1, column2, column3....)
VALUES (value1, value2, value3.....),(value1, value2, value3.....);
INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
注意:
1、指定的字段与值得顺序一一对应
2、字符串和日期型数据应该包含在括号里
3、插入数据的大小,应该在字段的规定范围内
UPDATE 表名 SET [column_name1= value1,... column_nameN = valueN] [WHERE 条件];
注意:where字句指定要修改的元组,如果没有[where 条件]
将会修改整个表内的数据
DELETE FROM 表名[WHERE 条件]
注意:DELETE语句不能删除某一个字段的值(可以使用UPDATA将值改为NULL)
执行顺序:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
SELECT
字段列表
FROM
表名字段
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后的条件列表
ORDER BY
排序字段列表
LIMIT
分页参数
1. 查询多个字段
SELECT 字段1,字段2,字段3... FROM 表名;
SELECT *FROM 表名;
2. 设置别名
可省略AS
SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;
SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;
3.去除重复记录
SELECT DISTINCT 字段列表 FROM 表名;
SELECT 字段列表 FROM 表名 WHERE 条件列表;
条件:
比较运算符 | 功能 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
<>或!= | 不等于 |
BETWEEN…AND… | 在 |
IN(…) | 在in之后的列表中的值,多选一 |
LIKE 占位符 | 模糊匹配( _匹配单个字符,%匹配任意个字符) |
IS NULL (is不能用=) | 是NULL |
IS NOT NULL | 不是NULL |
逻辑运算符 | 功能 |
---|---|
AND或&& | 并且,多个条件同时成立 |
OR或 ll | 或者(多个条件任意一个成立) |
NOT或 ! | 非,不是 |
注意:
1、一个汉字占用两个字符的位置,但是也有
“_”表示任何单个字符。一个汉字、一个字母、一个数字、一个符号、一个其它字符集的字符,都是一个字符。
2、ESCAPE
(查询DB_Design课程的某某属性)短语: WHERE Cname LIKE 'DB\_Design' ESCAPE '\'
定义’/'为转义字符,在_和%前面加上转义字符,使_和%不再为通配符,而是普通字符3、
% (百分号) 代表任意长度(长度可以为0)的字符串
例:a%b表示以a开头,以b结尾的任意长度的字符串。
4、_ (下横线) 代表任意单个字符
例:a_b表示以a开头,以b结尾的长度为3的任意字符串。如acb,afb等都满足该匹配串
-- 年龄等于30
select * from employee where age = 30;
-- 年龄小于30
select * from employee where age < 30;
-- 小于等于
select * from employee where age <= 30;
-- 没有身份证
select * from employee where idcard is null or idcard = '';
-- 有身份证
select * from employee where idcard;
select * from employee where idcard is not null;
-- 不等于
select * from employee where age != 30;
-- 年龄在20到30之间
select * from employee where age between 20 and 30;
select * from employee where age >= 20 and age <= 30;
-- 下面语句不报错,但查不到任何信息
select * from employee where age between 30 and 20;
-- 性别为女且年龄小于30
select * from employee where age < 30 and gender = '女';
-- 年龄等于25或30或35
select * from employee where age = 25 or age = 30 or age = 35;
select * from employee where age in (25, 30, 35);
-- 姓名为两个字
select * from employee where name like '__';
-- 身份证最后为X
select * from employee where idcard like '%X';
select * from employee where idcard like '__________X';(17个下划线)
IN的用法
查询信息系(IS)、数学系(MA)和计算机科学系(CS)
学生
WHERE Sdept IN ( 'IS','MA','CS' )
等价于
WHERE Sdept= ' IS ' OR Sdept= ' MA' OR Sdept= ' CS ';
常见的聚合函数:
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
SELECT 聚合函数(字段列表) FROM 表名;
注意:
所有的NULL值不参与聚合函数运算
COUNT(*)统计行数;可用来查询总人数
用DISTINCT以避免重复计算学生人数 :SELECT COUNT(DISTINCT Sno) FROM SC
-- 聚合函数
-- 1. 统计该企业员工数量
select count(id) from emp;
select count(*) from emp;
-- 2. 统计该企业员工的平均年龄
select avg(age) from emp;
-- 3. 统计该企业员工的最大年龄
select max(age) from emp;
-- 4. 统计该企业员工的最小年龄
select min(age) from emp;
-- 5. 统计西安地区员工的年龄之和
select sum(age) from emp where workaddress = '西安';
◆用途:
1、细化集函数的作用对象
——未对查询结果分组,集函数将作用于整个查询结果
——对查询结果分组后,集函数将分别作用于每个组
2、使用GROUP BY子句分组
——分组方法:按指定的一列或多列值分组,值相等的为一组
——使用GROUP BY子句后,SELECT子句的列名列表中只能出现分组属性和集函数
——GROUP BY子句的作用对象是查询的中间结果表
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];
WHERE与HAVING的区别:
执行时机不同:where是分组之前进行过滤,不满足where条件不参与分组;having是分组后对结果进行过滤。
判断条件不同:where不能对聚合函数进行判断,而having可以。
作用对象不同: WHERE子句作用于基表或视图,从中选择满足条件的元组;HAVING短语作用于组,从中选择满足条件的组
注意:
-- 根据性别分组,统计男性和女性数量(只显示分组数量,不显示哪个是男哪个是女)
select count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性数量
select gender, count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性的平均年龄
select gender, avg(age) from employee group by gender;
-- 年龄小于45,并根据工作地址分组
select workaddress, count(*) from employee where age < 45 group by workaddress;
-- 年龄小于45,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from employee where age < 45 group by workaddress having address_count >= 3;
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;
排序方式:
-- 根据年龄升序排序
SELECT * FROM employee ORDER BY age ASC;
SELECT * FROM employee ORDER BY age;
-- 两字段排序,根据年龄升序排序,若年龄相同,则按照入职时间降序排序
SELECT * FROM employee ORDER BY age ASC, entrydate DESC;
注意:
如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;
注意:
- 起始索引从0开始,
起始索引 = (查询页码 - 1) * 每页显示记录数
- 分页查询是数据库的方言,不同数据库有不同实现,MySQL是LIMIT
- 如果查询的是第一页数据,起始索引可以省略,直接简写 LIMIT 10
-- 查询第一页数据,每页展示10条
SELECT * FROM employee LIMIT 0, 10;
-- 查询第二页
SELECT * FROM employee LIMIT 10, 10;
-- DDL语句练习
-- 1. 年龄20,21,22,23的女性 (条件查询)
select * from emp where gender='女' and age in (20,21,22,23);
-- 2. 男性,年龄20-40(含),姓名三个字 (条件查询)
select * from emp where gender='男' and (age between 20 and 40) and name like '___';
-- 3. 年龄 < 60 的男女人数 (分组查询,聚合查询)
select gender, count(*) from emp where age < 60 group by gender;
-- 4. 年龄 <= 35 的姓名和年龄,年龄升序,若相同,按照入职时间降序 (排序查询)
select name,age from emp where age <= 35 order by age ASC, entrydate DESC;
-- 5. 性别男,年龄20-40(含)之内的前5个,年龄升序,若相同,按照入职时间升序 (排序查询,分页查询)
select name,gender,age,entrydate from emp where gender='男' and (age between 20 and 40) order by age ASC, entrydate ASC limit 0,5;
执行顺序:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
由于FROM执行顺序最靠前,因此,属性名改别名在FROM后面
数据控制亦称为数据保护
- 安全性控制
- 完整性控制
- 并发控制
- 恢复
SQL语言提供了数据控制功能,能够在一定程度上保证数据库中数据的:
DBMS实现存取控制的过程
- 用户或DBA把授权决定告知系统,这是由SQL的GRANT和REVOKE语句来完成的
- DBMS把授权的结果存入数据字典
- 当用户提出操作请求时,DBMS根据授权情况进行检查,以决定是否执行操作请求
总结:不同的用户对不同的数据应具有何种操作权力,是由DBA和表的建立者(即表的属主)根据具体情况决定的,SQL语言则为DBA和表的属主定义与回收这种权力提供了手段
USER mysql;
SELECT * FROM user;
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
注意:
若能在任意主机访问,将'主机名'用通配符'%'代替
此时客户端的连接语句为mysql -u 用户名 -p,但此时只是创建了用户,并没有赋予它访问权限,只能查询很少的数据库
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
DROP USER '用户名'@'主机名';
例子:
-- 创建用户itcast,只能在当前主机localhost访问,密码123456
create user 'itcast'@'localhost' identified by '123456';
-- 创建用户heima,能在任意主机访问,密码123456
create user 'heima'@'%' identified by '123456';
-- 修改heima密码1234
alter user 'heima'@'%' identified with mysql_native_password by '1234';
-- 删除用户itcast@localhost
drop user 'itcast'@'localhost';
常用的权限:
权限 | 说明 |
---|---|
ALL, ALL PRIVILEGES | 所有权限 |
SELECT | 查询数据 |
INSERT | 插入数据 |
UPDATA | 修改数据 |
DELETE | 删除数据 |
ALTER | 修改表 |
DROP | DROP 删除数据库/表/视图 |
CREATE | CREATE 创建数据库/表 |
用户的权限:
数据库的建立表(CREATETAB)的权限属于DBA,可由DBA授予普通用户,普通用户拥有此权限后可以建立基本表。
基本表或视图的属主拥有对该表或视图的一切操作权限。接受权限的用户:
- 一个或多个具体用户
- PUBLIC(全体用户)
WITH GRANT OPTION:
- 如果指定了WITH GRANT OPTION子句,则获得某种权限的用户还可以把这种权限再授予别的用户。
属主→用户1→用户2→用户3 此后不能再传播此特权- 如果没有指定WITH GRANT OPTION子句,则获得某种权限的用户只能使用该权限,但不能传播该权限
权限的级联回收:
- 系统将收回直接或间接从继承者获得的权限
- 所有授予出去的权力在必要时又都可以用REVOKE语句收回
SHOW GRANTS FOR '用户名'@'主机名';
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名'[WITH GRANT OPTION];
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
注意:
多个权限用逗号分隔
授权时,数据库名和表名可以用 * 进行通配,代表所有
改变后重新连接主机,才会有效
例子:
-- 查询权限
show grants for 'heima'@'%';
-- 授予权限 (授予针对于itcase这个数据库的所有表的所有权限)
grant all on itcase.* to 'heima'@'%';
-- 查询权限 (撤销针对于itcase这个数据库的所有表的所有权限)
revoke all on itcase.* from 'heima'@'%';
- 把查询Student表权限授给用户U1
GRANT SELECT ON TABLE Student to U1;
- 把对Student表和Course表的全部权限授予用户U2和U3
GRANT ALL PRIVILIGES ON TABLE Student, Course TO U2, U3;
- 把对表SC的查询权限授予所有用户
GRANT SELECT ON TABLE SC TO PUBLIC;
- 把查询Student表和修改学生姓名的权限授给用户U4
GRANT UPDATE(Sname), SELECT ON TABLE Student TO U4;
- DBA把在数据库S_C中建立表的权限授予用户U8
GRANT CREATEAB ON DATABASE S_C TO U8;
- 收回权限只需要将GRANT 和 TO 分别改成 REMOVE 和FROM
授予关于DATABASE的权限必须与授予关于TABLE的权限分开。
常用函数:
函数 | 功能 |
---|---|
CONCAT(s1, s2, …, sn) | 字符串拼接,将s1, s2, …, sn拼接成一个字符串 |
LOWER(str) | 将字符串全部转为小写 |
UPPER(str) | 将字符串全部转为大写 |
LPAD(str, n, pad) | 左填充,用字符串pad对str的左边进行填充,达到n个字符串长度 |
RPAD(str, n, pad) | 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度 |
TRIM(str) | 去掉字符串头部和尾部的空格 |
SUBSTRING(str, start, len) | 返回从字符串str从start位置起的len个长度的字符串 |
注意:如果在int型数据前面补0,是不显示的
-- 拼接 CONCAT(s1, s2, …, sn)
select concat('Hello','MySQL');
-- 小写 LOWER(str)
select lower('I AM SPIDERMAN');
-- 大写 UPPER(str)
select upper('i am spiderman');
-- 左填充 LPAD(str, n, pad)
select lpad('hellomysql',20,0);
-- 右填充 RPAD(str, n, pad)
select rpad('hellomysql',20,0);
-- 去除空格 TRIM(str)
select trim(' do you wanna build a snowman ');
-- 切片(起始索引为1) SUBSTRING(str, start, len)
select substr('howareuiamfinethanku',5,5);
-- 根据业务需求,企业员工的工号统一改为5位数,不足五位的前面用0补齐
UPDATE emp SET workno = lpad(workno,5,'0');
常用函数:
函数 | 功能 |
---|---|
CEIL(x) | 向上取整 |
FLOOR(x) | 向下取整 |
MOD(x, y) | 返回x/y的模 |
RAND() | 返回0~1内的随机数 |
ROUND(x, y) | 求参数x的四舍五入值,保留y位小数 |
-- 数值函数
-- CEIL(x) answer=2
select ceil(1.8);
-- FLOOR(x) answer=1
select floor(1.8);
-- MOD(x, y) (取余数)
select mod(4,3);
-- RAND()
select rand();
-- ROUND(x, y) 求参数x的四舍五入值,保留y位小数 answer=2.90
select round(2.899,2);
-- 生成随机6位数
select r/lpad(round(rand()*1000000,0),6,'0');
常用函数:
函数 | 表格 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
YEAR(date) | 获取指定date的年份 |
MONTH(date) | 获取指定date的月份 |
DAY(date) | 获取指定date的日期 |
DATE_ADD(date, INTERVAL expr type) | 返回一个日期/时间值加上一个时间间隔expr后的时间值 |
DATEDIFF(date1, date2) | 返回起始时间date1和结束时间date2之间的天数 |
-- CURDATE()
select curdate();
-- CURTIME()
select curtime();
-- NOW()
select now();
-- YEAR(date)-- MONTH(date)-- DAY(date)
select year(now());
select month(now());
select day(now());
-- DATE_ADD(date, INTERVAL expr type)
select date_add(now(),interval 10 day);
-- DATEDIFF(date1, date2)---date1-date2
select datediff(now(),'2000-04-01');
-- 查询所有员工的入职天数,并根据入职天数倒序排序
select name,datediff(now(),entrydate) from emp order by datediff(now(),entrydate) DESC;
--起个别名
select name,datediff(now(),entrydate) as entrydays from emp order by entrydays DESC;
常用函数:
函数 | 功能 |
---|---|
IF(value, t, f) | 如果value为true,则返回t,否则返回f |
IFNULL(value1, value2) | 如果value1不为空,返回value1,否则返回value2 |
CASE WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END | 如果val1为true,返回res1,… 否则返回default默认值 |
CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END | 如果expr的值等于val1,返回res1,… 否则返回default默认值 |
注意:
IFNULL()中,只有当value1=null的时候,才会返回value2的值,才会被默认为空,但当value=' '的时候,空格会被当成字符,仍会返回字符' '
-- IF(value, t, f)--如果value为true,则返回t,否则返回f
select if(false,'ok','error');
-- IFNULL(value1, value2)--如果value1不为空,返回value1,否则返回value2
select ifnull(null,'error');
select ifnull('ok','error');
-- CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END
-- 如果expr的值等于val1,返回res1,… 否则返回default默认值
-- 需求:查询emp表的员工姓名和工作地址(上海/北京--->一线城市,其他--->二线城市)
select
name,
(case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end) as '工作城市'
from emp;
-- CASE WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END
-- 如果val1为true,返回res1,… 否则返回default默认值
-- 统计班级成绩{ >=85优秀, >=60及格, else不及格 }
create table score(
id int comment 'ID',
name varchar(20) comment '姓名',
math int comment '数学',
english int comment '英文',
chinese int comment '语文'
) comment '学员成绩表';
insert into score(id, name, math, english, chinese)
values (1, 'Tom',67,88,95),(2,'Rose',23,66,90),(3,'Jack',56,98,76);
select
id,
name,
(case when math >=85 then '优秀' when math >=60 then '及格' else '不及格'end) as math,
(case when english >=85 then '优秀' when math >=60 then '及格' else '不及格'end) as english,
(case when chinese >=85 then '优秀' when math >=60 then '及格' else '不及格'end) as chinese
from score;
见聚合函数
约束是作用于表中字段上的,可以再创建表/修改表的时候添加约束
,用于限制存储在表中的数据约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制该字段的数据不能为null | NOT NULL |
唯一约束 | 保证该字段的所有数据都是唯一、不重复的 | UNIQUE |
主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | PRIMARY KEY(自增:AUTO_INCREMENT) |
默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 | DEFAULT |
检查约束(8.0.1版本后) | 保证字段值满足某一个条件 | CHECK |
外键约束 | 用来让两张图的数据之间建立连接,保证数据的一致性和完整性 | FOREIGN KEY |
create table user(
id int primary key auto_increment comment 'id主键',
name varchar(10) not null unique comment '姓名',
age int check ( age > 0 and age <= 120 ) comment '年龄',
status char(1) default '1' comment '状态',
gender char(1) comment '性别'
) comment '用户表';
--插入数据
insert into user(name, age,status,gender) values ('Tom1',19,'1','男'),('Tom2',25,'0','男');
!错误:
insert into user(name, age,status,gender) values (null,19,'1','男'),
错误原因:由unique限制的姓名唯一,导致已经申请了一次id,而不是null导致的,这也就解释了后面添加时主键自增值执行了一次,应该与运行顺序有关,null出发not null 直接报错不会申请空间,而unique则是在已经申请完空间后进行唯一判断
注意:
1、可以同时用多个约束,使用空格分开即可
2、auto_increment 自动增长不需要插入数据,会根据位置给定相应序号
3、即使有的变量DEFAULT,但也可以再次赋值,不一定就是default的值
约束条件 | 关键字 |
---|---|
主键 | PRIMARY KEY |
自动增长 | AUTO_INCREMENT |
不为空 | NOT NULL |
唯一 | UNIQUE |
逻辑条件 | CHECK |
默认值 | DEFAULT |
概念: 外键用来让两张表的数据之间进行连接,从而保证数据的一致性和完整性。
CREATE TABLE 表名(
字段名 字段类型,
...
[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 (可以自定义)FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);
注意:
1、外键在创建后,主表中的关联无法删除,这样保证了一致性和完整性
2、子表中的外键一定是父表中的主键,确保两个字段的约束条件一致
3、字表中关联的字段里面的数据一定要与父表中的数据一一对应,不能在父表中没有
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
行为 | 说明 |
---|---|
NO ACTION | 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与RESTRICT一致) |
RESTRICT | 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与NO ACTION一致) |
CASCADE | 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在子表中的记录 |
SET NULL | 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(要求该外键允许为null) |
SET DEFAULT | 父表有变更时,子表将外键设为一个默认值(Innodb不支持) |
更改删除/更新行为:
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;
案例:部门与员工
关系:一个部门对应多个员工,一个员工对应一个部门
实现:在多的一方建立外键,指向一的一方的主键
案例: 学生与课程
关系: 一个学生可以选多门课程,一门课程也可以供多个学生选修
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
案例:
-- 多对多 --------------------------------------------------------
create table student
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
insert into student
values (null, '黛绮丝', '2000100101'),
(null, '谢逊', '2000100102'),
(null, '殷天正', '2000100103'),
(null, '韦一笑', '2000100104');
create table course
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
insert into course
values (null, 'Java'),
(null, 'PHP'),
(null, 'MySQL'),
(null, 'Hadoop');
create table student_course
(
id int auto_increment comment '主键' primary key,
studentid int not null comment '学生ID',
courseid int not null comment '课程ID',
constraint fk_courseid foreign key (courseid) references course (id),
constraint fk_studentid foreign key (studentid) references student (id)
) comment '学生课程中间表';
insert into student_course
values (null, 1, 1),
(null, 1, 2),
(null, 1, 3),
(null, 2, 2),
(null, 2, 3),
(null, 3, 4);
案例: 用户与用户详情
关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率
实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
-- 一对一 -------------------------------------------------------------------
create table tb_user
(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
age int comment '年龄',
gender char(1) comment '1: 男 , 2: 女',
phone char(11) comment '手机号'
) comment '用户基本信息表';
create table tb_user_edu
(
id int auto_increment primary key comment '主键ID',
degree varchar(20) comment '学历',
major varchar(50) comment '专业',
primaryschool varchar(50) comment '小学',
middleschool varchar(50) comment '中学',
university varchar(50) comment '大学',
userid int unique comment '用户ID',
constraint fk_userid foreign key (userid) references tb_user (id)
) comment '用户教育信息表';
insert into tb_user(id, name, age, gender, phone)
values (null, '黄渤', 45, '1', '18800001111'),
(null, '冰冰', 35, '2', '18800002222'),
(null, '码云', 55, '1', '18800008888'),
(null, '李彦宏', 50, '1', '18800009999');
insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid)
values (null, '本科', '舞蹈', '静安区第一小学', '静安区第一中学', '北京舞蹈学院', 1),
(null, '硕士', '表演', '朝阳区第一小学', '朝阳区第一中学', '北京电影学院', 2),
(null, '本科', '英语', '杭州市第一小学', '杭州市第一中学', '杭州师范大学', 3),
(null, '本科', '应用数学', '阳泉第一小学', '阳泉区第一中学', '清华大学', 4);
table1=子表,table2=父表
显示语法:
select * from table1, table2;
隐式语法:
select * from table1 cross join table2;
表名前缀:任何子句中引用表1和表2中同名属性时,都必须加表名前 缀
select * from table1, table2 where table1.table2 = table2.id;
-- 查询拥有员工的部门ID、部门名称
select distinct e.dept_id, d.name from emp as e, dept as d where e.dept_id = d.id;
案例:
-- 创建dept表,并插入数据
create table dept
(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '部门名称'
) comment '部门表';
INSERT INTO dept (id, name)
VALUES (1, '研发部'),
(2, '市场部'),
(3, '财务部'),
(4, '销售部'),
(5, '总经办'),
(6, '人事部');
-- 创建emp表,并插入数据
create table emp
(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '姓名',
age int comment '年龄',
job varchar(20) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直属领导ID',
dept_id int comment '部门ID'
) comment '员工表';
-- 添加外键
alter table emp
add constraint fk_emp_dept_id foreign key (dept_id) references dept (id);
INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5),
(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),
(3, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1),
(4, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1),
(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),
(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1),
(7, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3),
(8, '周芷若', 19, '会计', 48000, '2006-06-02', 7, 3),
(9, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3),
(10, '赵敏', 20, '市场部总监', 12500, '2004-10-12', 1, 2),
(11, '鹿杖客', 56, '职员', 3750, '2006-10-03', 10, 2),
(12, '鹤笔翁', 19, '职员', 3750, '2007-05-09', 10, 2),
(13, '方东白', 19, '职员', 5500, '2009-02-12', 10, 2),
(14, '张三丰', 88, '销售总监', 14000, '2004-10-12', 1, 4),
(15, '俞莲舟', 38, '销售', 4600, '2004-10-12', 14, 4),
(16, '宋远桥', 40, '销售', 4600, '2004-10-12', 14, 4),
(17, '陈友谅', 42, null, 2000, '2011-10-12', 1, null);
-- 合并查询(笛卡尔积,会展示所有组合结果)
select * from emp, dept;
select * from emp, dept where emp.dept_id = dept.id;
内连接查询的是两张表交集的部分,交集指的是属性的值的交际,在两表中有相互对应的值,可用来筛选所有有工作部门的员工,而不查询工作部门为空的员工
SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;
案例:
-- 查询员工姓名,关联部门名称 (隐式)
SELECT emp.name, dept.name FROM emp, dept WHERE emp.dept_id = dept.id ;
SELECT e.name, d.name FROM emp e, dept d WHERE e.dept_id = d.id;
-- 如果为表起了别名,就不能再通过原表名来限定字段
-- 查询员工姓名,关联部门名称 (显式)
SELECT e.name, d.name FROM emp AS e JOIN dept AS d ON e.dept_id = d.id;
注意:FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
一旦FROM 后面改变了名字,那SELECT和WHERE都应该使用别名,不能再继续使用原名
第一种方法是使用泛用性高的方法来实现内连接
第二种方法是专为为了内连接设计的语法,可以方便的连接三个以上的表
常用来查询即使员工的某些值为空,也要求在表格中展示的情况
查询左表所有数据,以及两张表交集部分数据
等号左边那张表的内容都会被返回。如果左表中行在右表中没有匹配行,则结果中右表中的列返回空值
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;
查询右表所有数据,以及两张表交集部分数据
等号右边那张表的内容都会被返回。无论ON后面的条件是对还是错;如右表中行在左表中
没有匹配行,则结果中左表中的列返回空值
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;
案例:
-- 查询emp表和对应的部门信息(左外)
SELECT e.* , d.name FROM emp e LEFT JOIN dept d ON e.dept_id = d.id;
-- 查询dept表和对应的员工信息(右外)
SELECT d.* , e.* FROM dept d RIGHT OUTER JOIN emp e ON e.dept_id = d.id;
左连接可以查询到没有dept的employee,右连接可以查询到没有employee的dept 左外用的比较多
全连接(FULL JOIN)或全外连接(FULL OUTER JOIN)
返回左表和右表中的所有行。当某行在另一表
中没有匹配行,则另一表中的列返回空值
1、当前表与自身的连接查询,
自连接必须使用表别名
,要不然我们会不清楚条件和返回字段到底是哪一张表中的字段,如table1.table2 = table2.id;
2、由于所有属性名都是同名属性,因此必须使用别名前缀(表名.属性)
3、 自连接查询,可以是内连接查询,也可以是外连接查询
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;
案例:
--查询每一门课的直接先修课的课程名
SELECT a1.Cname,a2.Cname FROM Course a1,Course a2 WHERE a1.Cpno=a2.Cno
--查询每一门课的间接先修课的课程号(即先修课的先修课)
SELECT a1.Cno,a2.Cpno FROM Course a1,Course a2 WHERE a1.Cpno=a2.Cn
--查询同时选修2号课程和3号课程学生的学号
SELECT a.Sno
FROM SC a,SC b
WHERE a.Sno=b.Sno
and a.Cno<>b.Cno
and a.Cno=2
and b.Cno=3;
-- 查询员工 以及 所述领导的名字 (内连接自连接)
SELECT e1.name, e2.name FROM emp e1 JOIN emp e2 ON e1.managerid = e2.id;
-- 查询员工 以及 所述领导的名字, 如果无,也要显示 (外连接自连接)
SELECT e1.name, e2.name FROM emp e1 LEFT OUTER JOIN emp e2 ON e1.managerid = e2.id;
SELECT *
FROM orders o
NATURAL JOIN customers c -- 后面不用写具体的列名了。因为数据库自己会去猜
让数据库自己去选一列共同的列名,然后执行查询结果。
没办法控制,不推荐
把多次查询的结果合并,形成一个新的查询集
SELECT 字段列表 FROM 表A ...
UNION [ALL]
SELECT 字段列表 FROM 表B ...
注意事项:
1、UNION ALL 会有重复结果,UNION 不会
2、联合查询比使用or效率高,不会使索引失效
3、UNION 是结果合并,OR是查询条件判断
4、对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致
并操作(UNION)
交操作(INTERSECT)
差操作(MINUS)
场景案例5:
结合max聚合函数
写法1:
CASE SCORE WHEN 'A' THEN '优' ELSE '不及格' END
CASE SCORE WHEN 'B' THEN '良' ELSE '不及格' END
CASE SCORE WHEN 'C' THEN '中' ELSE '不及格' END
写法2:
CASE WHEN SCORE = 'A' THEN '优'
WHEN SCORE = 'B' THEN '良'
WHEN SCORE = 'C' THEN '中' ELSE '不及格' END
场景案例1:
SELECT
STUDENT_NAME,
(CASE WHEN score < 60 THEN '不及格'
WHEN score >= 60 AND score < 80 THEN '及格'
WHEN score >= 80 THEN '优秀'
ELSE '异常' END) AS REMARK
FROM
TABLE
场景案例2:
现老师要统计班中,有多少男同学,多少女同学,并统计男同学中有几人及格,女同学中有几人及格
SELECT
SUM (CASE WHEN STU_SEX = 0 THEN 1 ELSE 0 END) AS MALE_COUNT,
SUM (CASE WHEN STU_SEX = 1 THEN 1 ELSE 0 END) AS FEMALE_COUNT,
SUM (CASE WHEN STU_SCORE >= 60 AND STU_SEX = 0 THEN 1 ELSE 0 END) AS MALE_PASS,
SUM (CASE WHEN STU_SCORE >= 60 AND STU_SEX = 1 THEN 1 ELSE 0 END) AS FEMALE_PASS
FROM
THTF_STUDENTS
场景案例3:
现要求统计各个城市,总共使用了多少水耗、电耗、热耗,使用一条SQL语句输出结果
SELECT
E_CODE,
SUM(CASE WHEN E_TYPE = 0 THEN E_VALUE ELSE 0 END) AS WATER_ENERGY,--水耗
SUM(CASE WHEN E_TYPE = 1 THEN E_VALUE ELSE 0 END) AS ELE_ENERGY,--电耗
SUM(CASE WHEN E_TYPE = 2 THEN E_VALUE ELSE 0 END) AS HEAT_ENERGY--热耗
FROM
THTF_ENERGY_TEST
GROUP BY
E_CODE
场景案例4:
根据城市用电量多少,计算用电成本。假设电能耗单价分为三档,根据不同的能耗值,使用相应价格计算成本。
CASE WHEN energy <= (SELECT P_LIMIT FROM TABLE_PRICE WHERE P_LEVEL = 0) THEN (SELECT P_PRICE FROM TABLE_PRICE WHERE P_LEVEL = 0)
WHEN energy > (SELECT P_LIMIT FROM TABLE_PRICE WHERE P_LEVEL = 0) AND energy <= (SELECT P_LIMIT FROM TABLE_PRICE WHERE P_LEVEL = 1) THEN (SELECT P_PRICE FROM TABLE_PRICE WHERE P_LEVEL = 1)
WHEN energy > (SELECT P_LIMIT FROM TABLE_PRICE WHERE P_LEVEL = 1) AND energy <= (SELECT P_LIMIT FROM TABLE_PRICE WHERE P_LEVEL = 2) THEN (SELECT P_PRICE FROM TABLE_PRICE WHERE P_LEVEL = 2)
场景案例5:
结合max聚合函数
https://blog.csdn.net/rongtaoup/article/details/82183743
SQL语句中嵌套SELECT语句,称谓嵌套查询,又称子查询。
作用方法:
①不相关子查询:子查询的查询条件不依赖于父查询 由里向外逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
②相关子查询:子查询的查询条件依赖于父查询 首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表;然后再取外层表的下一个元组;重复这一过程,直至外层表全部检查完为止。
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);
子查询的限制:
1、子查询外部的语句可以是 INSERT / UPDATE / DELETE / SELECT 的任何一个
增、删、查、改
2、SELECT 之后属性也可以用子查询
3、不能使用ORDER BY子句
4、有些嵌套查询可以用连接运算替代
-- 11. 查询所有的部门信息, 并统计部门的员工人数
select d.id, d.name, count(e.dept_id) as numbers from emp e, dept d where e.dept_id = d.id group by e.dept_id;
select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数' from dept d;
根据子查询结果可以分为:
根据子查询位置可分为:
1、子查询返回的结果是单个值(数字、字符串、日期等)。
2、常用操作符:= <> > >= < <=
-- 标量子查询
-- 1. 查询销售部的所有员工信息
-- step 1: 查询销售部的部门ID
SELECT id FROM dept WHERE name = '销售部';
-- step 2: 根据ID,查询员工信息
select * from emp where dept_id = 4;
-- 一条就查询完
select * from emp where dept_id = (SELECT id FROM dept WHERE name = '销售部');
-- 查询在‘方东白’入职之后的员工信息
select entrydate from emp where name = '方东白' ;
select * from emp where entrydate > '2009-02-12';
select * from emp where entrydate > (select entrydate from emp where name = '方东白' );
返回的结果是一列或多行。
常用操作符:
操作符 | 描述 | 例子 |
---|---|---|
IN | 在指定的集合范围内,多选一 | |
NOT IN | 不在指定的集合范围内 | |
ANY | 子查询返回列表中,有任意一个满足即可 | |
SOME | 与ANY等同,使用SOME的地方都可以使用ANY | |
ALL | 子查询返回列表的所有值都必须满足 | |
比较运算符 | ||
EXISTS | 带有EXISTS谓词的子查询不返回任何实际数据,它只产生逻辑真值“true”或是逻辑假值“false” |
-- 列子查询
-- 1. 查询 "销售部" 和 "市场部" 的所有员工信息
-- step 1: 查询 "销售部" 和 "市场部" 的部门ID
select id from dept where name = '销售部' or name = '市场部';
-- step 2: 根据部门ID, 查询员工信息
select * from emp where dept_id = 2 or dept_id = 4;
-- 一条就查询完
select * from emp where dept_id = (select id from dept where name = '销售部' or name = '市场部');
-- 2. 查询比 财务部 所有人工资都高的员工信息
-- step 1: 查询所有 财务部 人员工
select id from dept where name = '财务部';
select salary from emp where dept_id = 3;
--二合一
select salary from emp where dept_id = (select id from dept where name = '财务部');
-- step 2: 比 财务部 所有人工资都高的员工信息
select * from emp where salary > all ( select salary from emp where dept_id = 3 );
select * from emp where salary > all ( select salary from emp where dept_id = (select id from dept where name = '财务部') );
-- 3. 查询比研发部其中任意一人工资高的员工信息
select id from dept where name = '研发部';
select salary from emp where dept_id = ( select id from dept where name = '研发部' );
select * from emp where salary > any ( select salary from emp where dept_id = ( select id from dept where name = '研发部' ) ) ;
1 返回的结果是一行(可以是多行)
2、常用操作符:=, <, >, IN, NOT IN
-- 行子查询
-- 1. 查询与‘张无忌’的薪资及领导相同的员工信息
select salary, managerid from emp where name = '张无忌';
select * from emp where salary = 12500 and managerid = 1;
-- 一条就查询完
select * from emp where (salary, managerid) = (12500,1);
select * from emp where (salary, managerid) = (select salary, managerid from emp where name = '张无忌');
where(属性1,属性2...属性n)=(值1,值2,...,值n);
1、返回的结果是多行多列
2、常用操作符: IN
-- 表子查询
-- 1. 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select job, salary from emp where name = '鹿杖客' or name = '宋远桥';
select * from emp where (job,salary) in (select job, salary from emp where name = '鹿杖客' or name = '宋远桥');
-- 2. 查询入职日期是 "2006-01-01" 之后的员工信息 , 及其部门信息
-- 入职日期是 "2006-01-01" 之后的员工信息
select * from emp where entrydate > '2006-01-01';
-- 把上一步查询出来的表起别名作为新表e,后引用左外连接连接上表和部门信息
select e.*, d.id from (select * from emp where entrydate > '2006-01-01') as e left join dept as d on e.dept_id = d.id;
案例:
-- 案例
-- 数据准备
create table salgrade
(
grade int comment'等级',
losal int comment'最小薪资',
hisal int comment'最大薪资'
) comment '薪资等级表';
insert into salgrade values (1, 0, 3000);
insert into salgrade values (2, 3001, 5000);
insert into salgrade values (3, 5001, 8000);
insert into salgrade values (4, 8001, 10000);
insert into salgrade values (5, 10001, 15000);
insert into salgrade values (6, 15001, 20000);
insert into salgrade values (7, 20001, 25000);
insert into salgrade values (8, 25001, 30000);
-- 1. 查询员工的姓名、年龄、职位、部门信息 (隐式内连接)
select e.name, e.age, e.job, d.name from emp as e, dept as d where e.dept_id = d.id;
-- 2. 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息 (显示内连接)
select e.name, e.age, e.job, d.name from emp as e, dept as d where e.dept_id = d.id and age < 30;
select e.name, e.age, e.job, d.name from emp as e inner join dept as d on e.dept_id = d.id where age < 30;
-- 3. 查询拥有员工的部门ID、部门名称
select distinct e.dept_id, d.name from emp as e, dept as d where e.dept_id = d.id;
-- 4. 查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来(左外连接)
select e.name, d.name from emp as e left join dept as d on e.dept_id = d.id where age > 40;
-- 5. 查询所有员工的工资等级
-- 表: emp , salgrade
-- 连接条件 : emp.salary >= salgrade.losal and emp.salary <= salgrade.hisal
select e.*, s.grade from emp as e, salgrade as s where e.salary >= s.losal and e.salary <= s.hisal;
-- 方式一
select e.*, s.grade, s.losal, s.hisal from emp e, salgrade s where e.salary >= s.losal and e.salary <= s.hisal;
-- 方式二
select e.*, s.grade, s.losal, s.hisal from emp e, salgrade s where e.salary between s.losal and s.hisal;
-- 6. 查询 "研发部" 所有员工的信息及 工资等级
-- 表: emp , salgrade,dep(三张表)
-- 连接条件 :emp.salary betweenn salgrade.losal and solgrade.hisal,emp.dept_id=dept.id
-- 查询条件 :dept.name='研发部'
select id from dept where name = '研发部';
select e.*, s.grade from emp e, dept d, salgrade s where e.dept_id = d.id and (e.salary between s.losal and s.hisal) and d.name = '研发部';
-- 7. 查询 "研发部" 员工的平均工资
select id from dept where name = '研发部';
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.id = (select id from dept where name = '研发部');
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发部';
-- 8. 查询工资比 "灭绝" 高的员工信息(标量子查询 )
select salary from emp where name = '灭绝';
select * from emp where salary > (select salary from emp where name = '灭绝');
-- 9. 查询比平均薪资高的员工信息
select avg(salary) from emp; select * from emp where salary > (select avg(salary) from emp);
-- 10. 查询低于本部门平均工资的员工信息 (假设查1号部门和2号部门)
select avg(e1.salary) from emp e1 where e1.dept_id = 1;
select avg(e1.salary) from emp e1 where e1.dept_id = 2;
select e2.* from emp e2 where e2.salary < (select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id);
-- 查找本部门平均薪资
(select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id);
-- 11. 查询所有的部门信息, 并统计部门的员工人数
select d.id, d.name, count(e.dept_id) as numbers from emp e, dept d where e.dept_id = d.id group by e.dept_id;
select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数' from dept d;
-- 12. 查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称
-- 表: student , course , student_course
-- 连接条件: student.id = student_course.studentid , course.id =student_course.courseid
select s.name, s.id, c.name from student s, course c, student_course sc where s.id = sc.studentid and c.id =sc.courseid;
数据库设计(Database Design,简记为DBD):构造最优的数据模型,建立数据库及其应用系统的过程。
目前数据库已用于各类应用系统, MIS、DSS、OAS等
数据库设计的优劣将直接影响应用系统的质量和运行
效果。
为了解决“软件危机”,在1968年首次提出“软件工程”的概念。(软件工程课程包括系统建模和信息系统分析和设计这两门课程)
软件生存期是指从软件的规划、研制、实现、投入运行后的维护,直到它被新的软件所取代而停止使用的整个期间。
以数据库为基础的信息系统通常称为数据库应用系统,它一般具有信息的采集、组织、加工、抽取、综合和传播等功能。
数据库应用系统的开发是一项软件工程,但又有自己特有的特点,所以特称为“数据库工程”。
概念设计的方法:
概念设计中最著名的方法就是实体联系方法(ER方法)
概念设计的结果是得到一个与DBMS无关的概念模型
总结:
ER模型,又称Entity Relationship Model, 实体联系模型
ER模型是一种概念模型,关系模型是一种逻辑模型
- 实体
—实体(Entity)是指数据对象,指应用中可以区别的客观存在的事物。
—实体集(Entity Set)是指同一类实体构成的集合。
—一般将实体、实体集等概念统称为实体。ER模型中提到的实体往往是指实体集。
—ER模型中实体用方框表示实体名为名词。- 属性
属性不能再具有需要描述的性质
属性不能与其他实体有联系
—实体的某一特性称为属性(Attribute),实体的属性值是数据库中存储的主要数据
—在一个实体中,能够惟一标识实体的属性或属性集称为“实体标识符”/“主键”/“主码”/“码”
—实体若干属性的一组特定值,确定了一个特定的实体
—在ER图中,属性用椭圆形框表示; 加下划线的属性为标识符
- 联系
— 联系(Relationship) 表示一个或多个实体之间的关联关系。
—联系集(Relationship Set)是指同一类联系构成的集合。
—将联系、联系集等统称为联系。
—在ER图中,联系用菱形框表示联系名为动词。
—联系的元数:一个联系涉及到的实体集个数,称为该联系的元数或度数(Degree)。
—联系类型: 限制参与联系的实体的数目
【二元联系类型】:1:1 ,1:n,m:n
【一元联系类型】:1:1 ,m:n
【三元联系类型】:
国内教材用数字比例表示联系的类型
① 设计局部ER模型
②设计全局ER模型
③ 全局ER模型的优化
① ER图转换成关系模式集的规则
步骤1(实体类型的转换):将每个实体类型转换成一个关系模式,实体的属性即为关系模式的属性,实体标识符即为关系模式的键。
②采用ER模型的逻辑设计步骤
数据建模: 即对于一个特定的应用,如何在数据库中表示数据
设计关系模型方法:
①关系模型设计理论
②概念设计模型: E/R——传统的和UML子集——目前常用的
UML
◆Unified Modeling Language,统一建模语言
◆UML 用于面向对象建模,但是现在也用于数据库建模
◆UML 与 E/R模型相似,但是不提供多元联系,都是二元联系
UML 和 E/R 术语对比
关系数据库逻辑设计:
- 针对一个具体问题,应如何构造一个适合于它的数据模式,即应该构造几个关系,每个关系由哪些属性组成等。
- 数据库逻辑设计的工具──关系数据库的规范化理论
有关关系的概念
关系模式的形式化定义
关系模式的简化表示
完整性约束的表现形式:
①限定属性取值范围:例如学生成绩必须在0-100之间
②定义属性值间的相互关连(主要体现于值的相等与否),这就是数据依赖,它是数据库模式设计的关键
.
数据依赖:
①是通过一个关系中属性间值的相等与否体现出来的数据间的相互关系
②是现实世界属性间相互联系的抽象
③ 是数据内在的性质
④是语义的体现
数据依赖的主要类型:
①函数依赖,(Functional Dependency,简记为FD)
②多值依赖,(Multivalued Dependency,简记为MVD)
③连接依赖
例子:
数据依赖引起的问题:
数据依赖对关系模式的影响
解决办法:规范化理论
① 规范化理论正是用来改造关系模式
② 通过分解关系模式来消除其中不合适的数据依赖
③ 以解决插入异常、删除异常、更新异常和数据冗余问题
第二范式的定义:
任何一个非主属性都不能依赖于码的一部分即不能依赖于码的真子集,而是全部的码。
采用投影分解法将一个1NF的关系分解为多个2NF的关系,可以在一定程度上减轻原1NF关系中存在的插入异常、删除异常、数据冗余度大、修改复杂等问题
将一个1NF关系分解为多个2NF的关系,并不能完全消除关系模式中的各种异常情况和数据冗余。
.
第三范式定义: 任何一个非主属性都不能传递依赖于码
第三范式基于第一范式,包含第二范式
若R∈3NF,则R的每一个非主属性既不部分函数依赖于候选码也不传递函数依赖于候选码。
如果R∈3NF,则R也是2NF。
采用投影分解法将一个2NF关系分解为多个3NF的关系后,已在很大程度上解决原2NF关系中存在的插入异常、删除异常、数据冗余度大、修改复杂等问题,但由于可能存在主属性对候选键的部分依赖和传递依赖,因此关系模式的分离仍不够彻底。
BCNF范式的定义: 非主属性和主属性都不能依赖于码的一部分或者传递依赖于码。
换句话说,在关系模式R中,箭头左边集都包含候选码
- 采用投影分解法将一个3NF的关系分解为多个BCNF的关系,可以进一步解决原3NF关系中可能存在的插入异常、删除异常、数据冗余度大、修改复杂等问题。
- BCNF的关系模式所具有的【性质】:
①所有非主属性都完全函数依赖于每个候选码
②所有主属性都完全函数依赖于每个不包含它的候选码
③没有任何属性完全函数依赖于非码的任何一组属性- 3NF与BCNF的关系:
①如果关系模式R∈BCNF,必定R∈3NF。
②如果R∈3NF,且R只有一个候选码,则R必属于BCNF。
- 如果一个关系数据库中的所有关系模式都属于BCNF,那么在函数依赖范畴内,它已实现了模式的彻底分解,达到了最高的规范化程度,消除了产生插入异常和删除异常的根源,而且数据冗余也减少到极小程度。
.
- 关系数据库的规范化理论是数据库逻辑设计的工具
- 一个关系只要其分量都是不可分的数据项,它就是规范化的关系,但这只是最基本的规范化
- 规范化程度可以有6个不同的级别,即6个范式
- 规范化程度过低的关系不能够很好地描述现实世界,可能会存在插入异常、删除异常、修改复杂、数据冗余等问题,解决方法就是对其进行规范化,转换成高级范式。
- 【定义】:一个低一级范式的关系模式,通过模式分解可以转换为若干个高一级范式的关系模式集合,这种过程就叫关系模式的规范化。
- 规范化的基本思想是逐步消除数据依赖中不合适的部分,使模式中的各关系模式达到某种程度的【分离】。
- 采用【一事一地】的模式设计原则,让一个关系描述一个概念、一个实体或者实体间的一种联系。
- 若多于一个概念就把它“分离”出去。因此所谓【规范化实质上是概念的单一化】。
- 不能说规范化程度越高的关系模式就越好,在设计数据库模式结构时,必须对现实世界的实际情况和用户应用需求作进一步分析,确定一个合适的、能够反映现实世界的模式。
- 上面的规范化步骤可以在其中任何一步终止。
- 关系模式的规范化过程是通过对关系模式的分解来实现的。
① 把低一级的关系模式分解为若干个高一级的关系模式的方法并不唯一。
② 在这些分解方法中,只有能够保证分解后的关系模式与原关系模式等价的方法才有意义。- 判断对关系模式的一个分解【是否与原关系模式等价的标准】
① 分解具有无损连接性,保证不丢失信息
② 分解要保持函数依赖,可以减轻或解决各种异常情况- 分解具有无损连接性和分解保持函数依赖是两个互相独立的标准
①具有无损连接性的分解不一定能够保持函数依赖
② 同样,保持函数依赖的分解也不一定具有无损连接性
例子: 关系模式 SL(Sno,Sdept,Sloc)的分解
方法一:
方法二:(有损连接)
方法三:(无损连接,但丢失了函数依赖)
具有无损连接的性的模式分解:
保持函数依赖的模式分解:
模式分解算法:
由规范化理论提供
- 若要求分解具有无损连接性,那么模式分解一定能够达到4NF
- 若要求分解保持函数依赖,那么模式分解定能够达到3NF,但不一定能够达到BCNF
- 若要求分解既具有无损连接性,又保持函数依赖,则模式分解一定能够达到3NF,但不一定能够达到BCNF
- 任何一个包含两个属性的关系模式一满足BCNF
- 安全性问题的提出: 数据库的一大特点是数据可以共享,但数据共享必然带来数据库的安全性问题,数据库系统中的数据共享不能是无条件的共享
- 数据库中数据的共享是在DBMS统一的严格的控制之下的共享,即只允许有合法使用权限的用户访问允许他存取的数据
- 数据库系统的安全保护措施是否有效是数据库系统主要的性能指标之一
- 【数据库的安全性定义】: 数据库的安全性是指保护数据库,防止因用户非法使用数据库造成数据泄露、更改或破坏。
- 【数据保密的定义】:数据保密是指用户合法地访问到机密数据后能否对这些数据保密。
通过制订法律道德准则和政策法规来保证。
用户非法使用数据库的情况:
用户编写一段合法的程序绕过DBMS及其授权机制,通过操作系统直接存取、修改或备份;
直接或编写应用程序执行非授权操作;
通过多次合法查询数据库,从中推导出一些保密数据
这些破坏安全性的行为可能是无意的,故意的,恶意的
安全性控制 就是要尽可能地杜绝所有可能的数据库非法访问,不管它们是有意的还是无意的。
计算机系统中常用的安全模型
安全控制的一般方法:
① 用户标识和鉴定
- (Identification & Authentication),系统提供的最外层安全保护措施
- 用户标识与鉴定的基本方法:
- 系统提供一定的方式让用户标识自己的名字或身份;
- 系统内部记录着所有合法用户的标识;
- 每次用户要求进入系统时,由系统核对用户提供的身份标识;
- 通过鉴定后才提供机器使用权;
- 用户标识和鉴定可以重复多次。
.
- 让用户标识自己的名字或身份的方法:
- 用户名/口令 :简单易行,但容易被人窃取
- 每个用户预先约定好一个计算过程或者函数
—系统提供一个随机数
—用户根据自己预先约定的计算过程或者函数进行计算
—系统根据用户计算结果是否正确鉴定用户身份② 存取控制
- 存取控制机制的功能: 保证用户只能访问其有权存取的数据
- 存取控制机制的组成:
- 定义存取权限:在数据库系统中,为了保证用户只能访问他有权存取的数据,必须预先对每个用户定义存取权限。
①存取权限由两个要素组成: 数据对象和操作类型
②定义一个用户可以在哪些数据对象上进行哪些类型的操作
③在数据库系统中,定义存取权限称为授权(Authorization)
④授权定义经过编译后存放在数据字典中
⑤关系系统中的存取权限
- 检查存取权限:对于通过鉴定获得访问权的用户(即合法用户),系统根据他的存取权限定义对他的各种操作请求进行控制,确保他只执行合法操作。
①对于获得连接权后又进一步发出存取数据库操作的用户
—DBMS查找数据字典,根据其存取权限对操作的合法性进行检查
—若用户的操作请求超出了定义的权限,系统将拒绝执行此操作
- 用户权限定义和合法权检查机制一起组成了DBMS的安全子系统
- 授权粒度 :指可以定义的数据对象的范围
①它是衡量授权机制是否灵活的一个重要指标
②授权定义中数据对象的粒度越细,即可以定义的数据对象的范围越小,授权子系统就越灵活。- 数据对象粒度
①关系数据库中授权的数据对象粒度
– 数据库
– 表
– 属性列
– 行
②能否提供与数据值有关的授权反映了授权子系统精巧程度- DBMS实现存取控制的过程
① 用户或DBA把授权决定告知系统,这是由SQL的GRANT和REVOKE语句来完成的
② DBMS把授权的结果存入数据字典
③ 当用户提出操作请求时,DBMS根据授权情况进行检查,以决定是否执行操作请求③ 视图
④ 审计
- 审计功能启用一个专用的审计日志(Audit Log),系统自动将用户对数据库的所有操作记录在上面;
- DBA可以利用审计日志中的追踪信息,重现导致数据库现有状况的一系列事件,以找出非法存取数据的人
- C2以上安全级别的DBMS必须具有审计功能
- 审计功能
① 审计很费时间和空间,所以DBMS往往都将其作为可选特征
② DBA可以根据应用对安全性的要求,灵活地打开或关闭审计功能
③ 用户识别和鉴定、存取控制、视图等安全性措施均为强制性机制,将用户操作限制在规定的安全范围内
④ 审计技术是预防手段,监测可能的不合法行为
⑤ 由于任何系统的安全性措施都不可能是完美无缺的,蓄意盗窃、破坏数据的人总是想方设法打破控制,所以,当数据相当敏感,或者对数据的处理极为重要时,就必须使用审计技术⑤ 密码存储
- 数据加密 :防止数据库中数据在存储和传输中失密的有效手段
- 加密的基本思想:
① 根据一定的算法将原始数据(明文,Plain text)变换为不可直接识别的格式(密文,Cipher text)
② 不知道解密算法的人无法获知数据的内容- 加密的方法:
① 替换方法: 使用密钥(Encryption Key)将明文中的每一个字符转换为密文中的一个字符
②置换方法:将明文的字符按不同的顺序重新排列
这两种方法结合能提供相当高的安全程度- 数据加密
①DBMS中的数据加密:
— 有些数据库产品提供了数据加密例行程序
— 有些数据库产品本身未提供加密程序,但提供了接口
② 数据加密功能通常也作为可选特征,允许用户自由选择
— 数据加密与解密是比较费时的操作,同时会占用大量系统资源
— 应该只对高度机密的数据加密
数据库的完整性定义:
- 数据库的完整性是指数据的正确性和相容性,防止不合
如:
-学生的年龄必须是整数,取值范围为14–29
-学生的性别只能是’男’或’女’
-学生的学号一定是唯一的
-学生所在的系必须是学校开设的系- 数据库是否具备完整性关系到数据库系统能否真实地反映现实世界,因此维护数据库的完整性是非常重要的
完整性控制机制
- 完整性约束条件定义机制
- 完整性约束条件是数据模型的一个重要组成部分,它约束了数据库中数据的语义
- DBMS应提供手段让用户根据现实世界的语义定义数据库的完整性约束条件,并把它们作为模式的一部分存入数据库中
- 完整性检查机制
- 检查用户发出的操作请求是否违背了完整性约束条件
- 违约反应
- 如果发现用户的操作请求使数据违背了完整性约束条件,则采取一定的动作来保证数据的完整性
完整性约束条件:
整个完整性控制都是围绕完整性约束条件进行的,完整性约束条件是完整性控制机制的核心
完整性约束条件作用的对象:
- 列 :对属性的取值类型、范围、精度等的约束条件
- 元组: 对元组中各个属性列间的联系的约束
- 关系: 对若干元组间、关系集合上以及关系之间的联系的约束
对象的状态:
- 静态:
-对静态对象的约束是反映数据库状态合理性
的约束
-这是最重要
的一类完整性约束- 动态:
-对动态对象的约束是反映数据库状态变迁
的约束
(3)修改被参照关系中主码的考虑
(4)参照完整性的实现结论
– RDBMS在实现参照完整性时,除了需要向用户提供定义主码、外码的机制
– 还需要向用户提供按照自己的应用要求选择处理依赖关系中对应的元组的方法。
一、SQL Server中的实体完整性
- SQL Server在CREATE TABLE语句中提供了PRIMARY KEY子句,供用户在建表时指定关系的主码列
- 在用PRIMARY KEY语句定义了关系的主码后,每当用户程序对主码列进行更新操作时,系统自动进行完整性检查
-– 违约操作
1.使主属性值为空值的操作
2.使主码值在表中不唯一的操作
-– 违约反应
1.系统拒绝此操作,从而保证了实体完整性二、SQL Server中的参照完整性
三、SQL Server中的用户定义的完整性
- 用CREATE TABLE语句在建表时定义用户完整性约束
——可定义三类完整性约束:
事务是并发控制和恢复的基本单位
数据库并发访问:
- 对多用户并发存取同一数据的操作不加控制可能会存取和存储不正常的数据
- DBMS必须提供并发控制机制
概念:
- 从用户的观点看,对数据库的某些操作应是一个整体,也就是一个独立的工作单元,不能分割
**例:**电子资金转账(从账号A转一笔款子到账号B)客户可能认为是一个独立的操作,而在DBS中是由几个操作组成的- 事务(Transaction) 是用户定义的一个
数据库操作序列
,这些操作要么全做,要么全不做,是一个不可分割的工作单位- 事务和程序是两个概念
– 在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序
– 一个应用程序通常包含多个事务- 事务是恢复和并发控制的基本单位
定义事务的两种方式:
一、显式方式
- 事务的开始由用户显式控制或DBMS自动隐含
- 事务结束由用户显式控制
begin transaction SQL语句1 SQL语句2 ... commit
begin transaction SQL语句1 SQL语句2 ... rollback
二、隐式方式
- 当用户没有显式地定义事务时,由DBMS按缺省规定自动划分事务
事务的ACID特性:
(1)原子性
事务是数据库的逻辑工作单位,事务中包括的诸操作要么都做,要么都不做
(2)一致性
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。(3)隔离性
一个事务的执行不能被其他事务干扰
- 一个事务内部的操作及使用的数据对其他并发事务是隔离的
- 并发执行的各个事务之间不能互相干扰
(4)持续性
持续性也称永加粗样式久性(Permanence)
- 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的
- 接下来的其他操作或故障不应该对其执行结果有任何影响
.
总结:
- 保证事务ACID特性是事务处理的重要任务
- 破坏事务ACID特性的因素:
①多个事务并行运行时,不同事务的操作交叉执行
DBMS必须保证多个事务的交叉运行不影响这些事务ACID特性,特别是原子性和隔离性
②事务在运行过程中被强行停止
DBMS必须保证被强行终止的事务对数据库和其他事务没有任何影响
- 这些就是DBMS中恢复机制和并发控制机制的责任
多事务执行方式
一、事务串行执行(serial)
- 每个时刻只有一个事务运行,其他事务必须等到这个事务结束以后方能运行
- 不能充分利用系统资源,发挥数据库共享资源的特点
二、交叉并发方式(interleaved concurrency)
- 事务的并行执行是这些并行事务的并行操作轮流交叉运行
- 是单处理机系统中的并发方式,能够减少处理机的空闲时间,提高系统的效率
三、同时并发方式(simultaneous concurrency)
- 多处理机系统中,每个处理机可以运行一个事务,多个处理机可以同时运行多个事务,实现多个事务真正的并行运行
- 最理想的并发方式,但受制于硬件环境
事务并发执行带来的问题
并发操作带来的数据不一致性
- 计算机系统对并行事务中并行操作的调度是随机的,而不同的调度可能会产生不同的结果
- 将所有事务串行起来的调度策略一定是正确的调度策略
如果一个事务运行过程中没有其他事务在同时运行 也就是说它没有受到其他事务的干扰那么就可以认为该事务的运行结果是正常的或者预想的
一、可串行化的调度
- 以不同的顺序串行执行事务也有可能会产生不同的结果,但由于不会将数据库置于不一致状态,所以都可以认为是正确的
- 几个事务的并行执行是正确的
当且仅当其结果与按某一次序串行地执行它们时的结果相同。这种并行调度策略称为可串行化(Serializable)的调度二、并发操作的调度
上述例子采用不同调度策略:
- 串行执行
– a.串行调度策略
– b.串行调度策略- 交错执行
– c.不可串行化的调度
– d.可串行化的调度
.可串行化的调度策略
- 为了保证并行操作的正确性,DBMS的并行控制机制必须提供一定的手段来保证调度是可串行化的
- 从理论上讲,在某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这也是最简单的调度策略
- 但这种方法实际上是不可行的,因为它使用户不能充分共享数据库资源
例:
例二 A:只执行T2 B:先执行T2,再执行T1 C:先执行T1再执行T2
保证并发操作调度正确性的方法
- **封锁方法:**两段锁(2PL)协议
- 时标方法
- 乐观方法
封锁概念:
- 封锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁;
- 加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象;
- 封锁是实现并发控制的一个非常重要的技术
封锁的类型
封锁的粒度
- X锁和S锁都是加在某一个数据对象上的
- 封锁的数据对象可以是逻辑单元
– 属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等- 封锁的数据对象也可以是物理单元
– 页(数据页或索引页)、块等- 封锁对象可以很大也可以很小
– 对整个数据库加锁
– 对某个属性值加锁- 封锁对象的大小称为封锁的粒度
----封锁粒度与系统的并发度和并发控制的开销密切相关
----选择封锁粒度时必须同时考虑开销和并发度两个因素,进行权衡,以求得最优的效果- 封锁粒度一般原则
1.需要处理大量元组的用户事务: 以关系为封锁单元;
2.需要处理多个关系的大量元组的用户事务:以数据库为封锁单位;
3.只处理少量元组的用户事务:以元组为封锁单位
封锁的协议
在运用X锁和S锁对数据对象加锁时,需要约定一些规则,这些规则为封锁协议(Locking Protocol)
- 何时申请X锁或S锁
- 持锁时间
- 何时释放
.两种封锁协议
三级封锁协议
一、1级封锁协议
- 事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放
– 正常结束(COMMIT)
– 非正常结束(ROLLBACK)- 1级封锁协议可防止丢失修改,并保证事务T是可恢复的
- 在1级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读“脏”数据。
二、2级封锁协议
三、3级封锁协议
两段封锁协议
可串行性是并行调度正确性的唯一准则
两段锁(2PL)协议就是为保证并行调度可串行性而提供的封锁协议
.
两段锁协议的内容:
- 1.在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁
- 2.在释放一个封锁之后,事务不再获得任何其他封锁
事务分为两个阶段:
- 第一阶段是获得封锁,也称为扩展阶段;
- 第二阶段是释放封锁,也称为收缩阶段。
总结:
- 并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的
- 所有遵守两段锁协议的事务,并行执行的结果一定是正确的
- 事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件,即可串行化的调度中,不一定所有事务都必须符合两段锁协议
封锁技术可以有效地解决并行操作的一致性问题, 但也带来一些新的问题。
如何避免活锁 : 采用先来先服务的策略
- 当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对这些事务排队
- 该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁
.解决死锁的两类方法
一、 死锁的预防
① 一次封锁法:
- 一次封锁法要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。
- 存在问题:
(1) 降低并发度: 一次就将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度
(2) 难以事先精确确定封锁对象: 数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难事先精确地确定每个事务所要封锁的数据对象
.② 顺序封锁法:
- 顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。
- 存在问题: 难于实现,事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁。
例如: 规定数据对象的封锁顺序为A,B,C,D,E。事务T3起初要求封锁数据对象B,C,E,但当它封锁了B,C后,才发现还需要封锁A,这样就破坏了封锁顺序.
.总结:
- 在操作系统中广为采用的预防死锁的策略并不很适合数据库的特点
- DBMS在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法
- 预防死锁为何能解决死锁?
产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死等待。预防死锁的发生就是要破坏产生死锁的条件
.二、死锁的诊断与解除
由DBMS的并发控制子系统定期检测系统中是否存在死锁,一旦检测到死锁,就要设法解除
.
检查方法:① 超时法
- 如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
- 优点: 实现简单
- 缺点:
(1)有可能误判死机
(2)时限若设置得太长,死锁发生后不能及时发现
.② 等待图法
- 用事务等待图动态反映所有事务的等待情况
• 事务等待图是一个有向图G=(T,U)
• T为结点的集合,每个结点表示正运行的事务
• U为边的集合,每条边表示事务等待的情况
• 若T1等待T2,则T1,T2之间划一条有向边,从T1指向T2- 并发控制子系统周期性地(比如每隔1 min)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁
.解除方法:
解除死锁:
- 选择一个处理死锁代价最小的事务,将其撤消
- 释放此事务持有的所有的锁,使其它事务能继续运行下去
一、故障是不可避免的,常见的故障有以下几种:
① 计算机硬件故障
②系统软件和应用软件的错误
③操作员的失误
④恶意破坏二、故障的影响:
①轻则造成运行事务非正常中断,影响数据库中数据的正确性
②重则破坏数据库,使数据库中数据部分或全部丢失
三、数据库的可恢复性: 系统能把数据库从被破坏、不正确的状态恢复到最近一个正确的状态。
四、DBMS对故障的对策 :
① DBMS恢复子系统,用来保证各种故障发生后,能把数据库中的数据从错误状态恢复到某种逻辑一致的状态。
② 即保证各个事务中的操作要么全部完成,要么全部不做。
③数据库系统所采用的恢复技术是否行之有效是衡量系统性能优劣的重要指标。
数据库故障分类:
一、事务故障
1、事务是数据库的基本工作单位
2、事务故障: 某个事务在运行过程中由于种种原因未运行至正常终止点就夭折了。
3、事务故障的常见原因:
①运算溢出
②输入数据有误
③违反了某些完整性限制
④某些应用程序出错
⑤并行事务发生死锁
.
二、系统故障
1、系统故障: 由于某种原因造成整个系统的正常运行突然停止,致使所有正在运行的事务都以非正常方式终止。
2、发生系统故障时,内存中数据库缓冲区的信息全部丢失;但存储在外部存储设备上的数据未受影响。
3、系统故障的常见原因:
①操作系统或DBMS代码错误
②操作员操作失误
③特定类型的硬件错误(如CPU故障)
④突然停电
.
三、介质故障
1、介质故障: 硬件故障使存储在外存中的数据部分丢失或全部丢失。
2、介质故障比前两类故障的可能性小得多,但破坏性最大。
3、介质故障的常见原因:
①硬件故障:磁盘损坏或 磁头碰撞
②操作系统的某种潜在错误
③瞬时强磁场干扰
数据库故障总结:
恢复技术的原理: 利用存储在系统其它地方的冗余数据来修复或重建数据库中被破坏的或不正确的数据。
恢复的实现技术: 复杂。大型数据库产品,恢复子系统的代码要占全部代码的10%以上。
恢复机制涉及的关键问题:
(1)如何建立冗余数据
- 数据转储
- 登记日志文件
(2)如何利用这些冗余数据实施数据库恢复
转储: 是指DBA将整个数据库复制到磁带或另一个磁盘上保存起来的过程。
后备副本: 这些备用的数据文本称为后备副本或后援副本
.
转储的用途:
1、供介质故障恢复时使用:一旦系统发生介质故障,数据库遭到破坏,可以将后备副本重新装入,把数据库恢复起来。
2、恢复的程度:
①重装后备副本只能将DB恢复到转储时的状态。
②要想恢复到故障发生时的状态,必须重新运行自转储以后的所有更新事务。
例:
转储方法:
(1) 静态转储与动态转储
静态转储: 是在系统中无运行事务时进行的转储操作
①转储操作开始的时刻,数据库处于一致性状态
②转储期间不允许(或不存在)对数据库的任何存取、修改活动
③静态转储得到的一定是一个数据一致性的副本
静态转储的优点: 实现简单
静态转储的缺点: 降低了数据库的可用性
——转储必须等待用户事务结束才能进行
—— 新的事务必须等待转储结束才能执行
利用静态转储副本进行恢复: 只需要把静态转储得到的后备副本装入,就能把数据库恢复到转储时刻的正确状态。
动态转储: 动态转储是指转储操作与用户事务并发进行,转储期间允许对数据库进行存取或修改。
动态转储的优点:
——不用等待正在运行的用户事务结束
——不会影响新事务的运行
动态转储的缺点: 不能保证副本中的数据正确有效
利用动态转储副本进行恢复: 需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件; 后备副本加上日志文件才能把数据库恢复到某一时刻的正确状态。
(2) 海量转储与增量转储
海量转储: 每次转储全部数据库
增量转储: 只转储上次转储后更新过的数据
海量转储与增量转储比较:
①从恢复角度看,使用海量转储得到的后备副本进行恢复往往更方便;
②但如果数据库很大,事务处理又十分频繁,则增量转储方式更实用更有效
总结:
转储方法分类
.
转储策略:
①从恢复方便角度看,应经常进行数据转储,制作后备副本
②但转储又是十分耗费时间和资源的,不能频繁进行
③DBA应根据数据库使用情况确定适当的转储周期和转储方法
④例:
– 每天晚上进行动态增量转储
– 每周进行一次动态海量转储
– 每月进行一次静态海量转储
日志文件的内容:
日志文件(log): 是用来记录事务对数据库的更新操作的文件
日志文件的格式 :
-以记录为单位的日志文件
-以数据块为单位的日志文件
日志文件内容:
– 各个事务的开始标记(BEGIN TRANSACTION)
– 各个事务的结束标记(COMMIT或ROLLBACK)
– 各个事务的所有更新操作
作为日志文件中的一个日志记录(log record)
.
基于记录的日志文件:每条日志记录的内容
• 事务标识(标明是那个事务)
• 操作类型(插入、删除或修改)
• 操作对象
• 更新前数据的旧值(对插入操作而言,此项为空值)
• 更新后数据的新值(对删除操作而言, 此项为空值)
.
基于数据块的日志文件: 每条日志记录的内容
• 事务标识(标明是那个事务)
• 更新前数据所在的整个数据块的值(对插入操作而言,此项为空值)
• 更新后整个数据块的值(对删除操作而言, 此项为空值)
日志文件的用途:
1、进行事务故障恢复
2、进行系统故障恢复
3、协助后备副本进行介质故障恢复日志文件与静态转储后备副本配合进行介质故障恢复
① 静态转储的数据已是一致性的数据
② 如果静态转储完成后,仍能定期转储日志文件,则在出现介质故障重装数据副本后,可以利用这些日志文件副本对已完成的事务进行重做处理。
③ 这样不必重新运行那些已完成的事务程序就可把数据库恢复到故障前某一时刻的正确状态。
日志文件与动态转储后备副本配合进行介质故障恢复
①动态转储机制在转储数据库时,必须同时转储同一时间点的日志文件,后备副本与该日志文件结合起来才能将数据库恢复到一致性状态。
② 与静态转储一样,如果动态转储完成后,仍能定期转储日志文件,则在做介质故障恢复时,可以利用这些日志文件副本进一步恢复数据库,避免重新运行事务程序。
登记日志文件的原则
为保证数据库是可恢复的,登记的次序严格按并行事务执行的时间次序:必须先写日志文件,后写数据库。
为什么要先写日志文件?
①写数据库和写日志文件是两个不同的操作,有可能在这两个操作之间发生故障,即这两个写操作只完成了一个;
②如果先写了数据库修改,而在日志文件中没有登记下这个修改,则以后就无法恢复这个修改了;
③ 如果先写日志,但没有修改数据库,按日志文件恢复时只不过是多执行一次不必要的UNDO操作,并不会影响数据库的正确性。
(1) 事务故障的恢复
事务故障是指事务在运行至正常终止点前被中止
恢复方法: 由恢复子系统应利用日志文件撤消(UNDO)此事务已对数据库进行的修改
ps: 事务故障的恢复由系统自动完成,不需要用户干预。
恢复步骤:
- 反向扫描文件日志(即从最后向前扫描日志文件),查找该事务的更新操作
- 对该事务的更新操作执行逆操作。即将日志记录中“更新前的值”写入数据库
–① 如果记录中是插入操作,则相当于做删除操作(因为此时“更新前的值”为空)
–② 若记录中是删除操作,则相当于做插入操作(因为此时“更新后的值”为空)
–③ 若是修改操作,则相当于用修改前值代替修改后值- 继续反向扫描日志文件,查找该事务的其他更新操作,并做同样处理
- 如此处理下去,直至读到此事务的开始标记,事务故障恢复就完成了。
.(2) 系统故障的恢复
系统故障造成数据库不一致状态的原因:
① 一些未完成事务对数据库的更新已写入数据库
②一些已提交事务对数据库的更新还留在缓冲区没来得及写入数据库
恢复方法:
①撤消故障发生时未完成的事务
②重做已完成的事务
ps: 系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
恢复步骤:
- 正向扫描日志文件(即从头扫描日志文件)
– ①找出在故障发生前已经提交的事务,将事务标识记入重做队列
–② 同时找出故障发生时尚未完成的事务,将事务标识记入撤消队列- 对撤消队列中的各个事务进行撤消(UNDO)处理
—反向扫描日志文件,对每个UNDO事务的更新操作执行逆操作,即将日志记录中“更新前的值”写入数据库- 对重做队列中的各个事务进行重做(REDO)处理
— 正向扫描日志文件,对每个REDO事务重新执行登记的操作。即将日志记录中“更新后的值”写入数据库.
.(3) 介质故障的恢复
介质故障: 发生介质故障后,磁盘上的物理数据和日志文件被破坏,这是最严重的一种故障。
ps: 介质故障的恢复需要DBA介入,具体的恢复操作仍由DBMS完成。
DBA的工作:
①重装最近转储的数据库副本和有关的各日志文件副本
②执行系统提供的恢复命令
恢复方法:
①重装数据库,使数据库恢复到一致性状态
②重做已完成的事务
恢复步骤:
本章总结:
- 复制是数据库更具容错性的方法,主要用于分布式结构的数据库中。
- 在多个场地保留多个数据库备份,可以是整个数据库的副本,也可以是部分数据库的副本
- 各个场地的用户可以并发存取不同的数据库副本,进一步提高系统并发度
- 但DBMS必须采取一定手段,保证用户对数据库的修改能够及时反映到其所有副本上
- 数据复制:
① 当数据库出现故障,系统可以用副本对其进行联机恢复
② 在恢复过程中,用户可以继续访问该数据库的副本,而不必中断其应用- 数据库复制三种方式:
①对等复制
②主从复制
③级联复制- 不同的复制方式提供了不同程度的数据一致性
- DBMS在使用复制技术时必须做到:
(1) 数据库复制必须对用户透明
(2) 主数据库和各个从复制数据库在任何时候都必须保持事务的完整性;
(3) 对于对异步的可在任何地方更新的复制方式,当两个应用在两个场地同时更新同一个记录,一个场地的更新事务尚未复制到另一个场地时,第二个场地已开始更新,这时可能引起冲突。
– DBMS必须提供控制冲突的方法,包括各种形式的自动解决方法和人工干预方法。
(1) 对等复制(最理想的复制方式)
(2) 主/从复制
(3)级联复制
- 介质故障是对系统影响最为严重的一种故障,严重影响数据库的可用性。
- 介质故障恢复比较费时,为预防介质故障,DBA必须周期性地转储数据库。
- 提高数据库可用性的解决方案
①日志文件
②数据库镜像- 什么是数据库镜像:
①DBMS自动把整个数据库或其中的关键数据复制到另一个磁盘上
②每当主数据库更新时,DBMS会自动把更新后的数据复制过去
③即DBMS自动保证镜像数据与主数据的一致性- 数据库镜像的用途:
① 出现介质故障时:
– DBMS自动利用镜像磁盘数据进行数据库的恢复
– 不需要关闭系统和重装数据库副本
②没有出现故障时:
– 可用于并发操作
• 一个用户对数据加排他锁修改数据
• 其他用户可以读镜像数据库上的数据,而不必等待该用户释放锁- 数据库镜像图
- 使用数据库镜像时注意
①数据库镜像是通过复制数据实现的,频繁地复制数据会降低系统运行效率;
②在实际应用中用户往往只选择对关键数据镜像, 如日志文件。
事务是一组操作的集合,事务会把所有操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败.
默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务
例如:银行转账
-- 数据准备
create table account{
id int auto_increment key comment'主键ID',
name varchar(10)comment'姓名',
money int comment'余额'
} comment'账户表';
-- 转账操作
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
程序抛出异常...(强行异常)
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
-- 恢复数据
update account set money = 2000 where name = '李四' or name = '张三';
--查看事务的提交方式
SELECT @@AUTOCOMMIT;
如果=1,即自动提交;如果=0,即手动提交
--设置事务的提交方式
SET @@autocommit=0;
-- 此时为手动提交
(若开启事务,则不需要设置提交方式)
方式一:设置事务提交方式——提交事务——回滚事务
方式二: 开启事务——提交事务——回滚事务
START TRANSACTION
或
BEGIN TRANSACTION;
COMMIT;
-- 一开始没执行提交时是没有反应的,执行了提交,才会有下一步
注意:若是用cmd,要重新连接数据库,此时的cmd查询到的是事务还未提交时的内容
ROLLBACK;
-- 一旦出错
-- 回滚事务,数据恢复,保证其正确性和完整性
案例优化:
-- 方式二
set @@autocommit = 1;
-- 自动提交
select @@autocommit;
-- 转账操作
start transaction ;
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
程序执行报错...
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
我们创建的数据库一般存储在C:/PromgramData/MySQL/MySQL server8.0/Data
常见问题:
问题 | 描述 | 详情 |
---|---|---|
脏读 | 一个事务读到另一个事务还没提交的数据 (后者还未确定提交,前者就已经读到了改变后的数据) | |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 | |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在 |
打开两个cmd窗口,可以 模拟两个客户端,两个并发事务
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
● √ 表示在当前隔离级别下该问题会出现,×表示这些问题都能够在当前隔离级别下解决
● Serializable 性能最低,数据安全性最高;Read uncommitted 性能最高,数据安全性最差
注意:事务隔离级别越高,数据越安全,但是性能越低
查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
-- SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效