目录
三: 查询语句 (select [distinct] 列名 from 表名)
十: 游标的使用,和function、procedure 一起使用
一: 数据库基本操作
-
-- 登入数据库 mysql -h localhost -u root -p
-
-- 创建数据库 create database 数据库名; create database if not exists 数据库名;
-
-- 删除数据库 drop database 数据库名;
drop databases if exists 数据库名;
-
-- 创建带字符集和排序规则的数据库 create database if not exists 数据库名 character set = 'utf8mb4' collate = 'utf8mb4_general_ci';
-
-- 切换当前数据库到别的数据库中 use 数据库名称;
二: 表的基本操作
增:
insert into 表名(列1, 列2, ...) values (列1的值,列2的值, ...);
删:
delete from 表名 [WHERE 表达式];
改:
update 表名 set 列1=值1, 列2=值2, ..., 列n=值n [where 布尔表达式];
查:
SELECT [DISTINCT] 查询列表 [FROM 表名] [WHERE 布尔表达式] [GROUP BY 分组列表 ] [HAVING 分组过滤条件] [ORDER BY 排序列表] [LIMIT 开始⾏, 限制条数] ;
-
如果不存在则创建表:
create table if not exists 表名 (
列名1 数据类型 [列的属性] comment '列注释',
...
列名n 数据类型 [列的属性]
) comment '表的注释信息';
-
备份表
create table 表名_bak select * from 表名;
-
删除表:
drop table 表名;
如果存在则删除表:
drop table if exists 表名;
-
-- 删除表中数据:
delete from 表名 [WHERE 表达式];
-
修改表名:
alter table 旧表名 rename to 新表名;
批量修改表明:
rename table 旧表名1 to 新表名1, 旧表名2 to 新表名2, ... 旧表名n to 新表名n;
-
将表从这个数据库转移到另一个数据库下(还可以改名):
alter table student1 rename to buba01.student3; alter table buba01.student3 rename to buba.student1;
-
增加表中的列:
alter table 表名 add column 列名 数据类型 [列的属性];
添加到第一列:
alter table 表名 add column 列名 列的类型 [列的属性] first;
添加到指定列的后边:
alter table 表名 add column 列名 列的类型 [列的属性] after指定列名;
-
修改列的信息
① alter table 表名 modify 列名 新数据类型 [新属性];
② alter table 表名 change 旧列名 新列名 新数据类型 [新属性];
-
⼀条语句中包含多个修改操作
alter table 表名 drop column 列名1,
drop column 列名2;
-
-- 创建完表之后,后续再添加外键 alter table ums_client_user_cost add constraint fk_UMS_CLIENT_USER_COST_UMS_CLIENT_USER_1 foreign key(client_user_id) references ums_client_user(id);
-
给表名或者列名起别名 as 命名
为表来定义别名,格式与定义列的别名⼀致,都是⽤空⽩字符或者 AS 隔开,这个在表名特别⻓的情况下可以让语句表达更清晰⼀些。
三: 查询语句 (select [distinct] 列名 from 表名)
-
查看某个表⾥已经存储了哪些数据:
select * from 表名;
-
查看mysql中的数据库
show databases;
-
查看数据库中的存储引擎
show engines;
-
查看mysql数据库中的表
show tables;
-
查询当前系统的日期时间: select now() from dual;
-
查询某个时间点距离现在多少天
select ename,datediff(now(),hiredate) from emp;
-
查看表结构:
desc 表名;
show create table 表名;
-
查询单个列 as 给列名起一个列的别名
select 列名 as 列名别名 from 表名;
或者 select 列名 from 表名;
-
查询多个列
select 列名1, 列名2, ... from 表名;
-
查询结果去重 distinct
① 去除单列中的重复结果:
select distinct 列名 from 表名;
② 去除多列的重复结果
select distinct 列名1, 列名2, ... 列名n from 表名;
-
限制查询结果条数 从0开始计数 (放在语句最后边)
limit n 查看前n条数据
limit 开始⾏, 限制条数;
select * from student_info limit 0,3;
分页计算: limit (页码 - 1) * 每页数据数量, 每页数据数量
-
对查询结果排序
① 按照单个列的值进⾏排序 asc升序 desc降序
order by 列名 asc|desc
select number,name from student_info order by number desc;
-- 如果不指定排序⽅向,则默认使⽤的是 asc,也就是从⼩到⼤的升序规则
② 按照多个列的值进⾏排序 (不写asc|desc排序,默认升序) select 列1,列2,... from 表名 order by 列1, 列2 asc|desc ...
-
order by 语句和 limit 语句结合使⽤,不过 order by 语句必须放在 limit 语句前边
☆重点: 简单查询语句中各⼦句的顺序☆
SELECT [DISTINCT] 查询列表
[FROM 表名]
[WHERE 布尔表达式]
[GROUP BY 分组列表 ]
[HAVING 分组过滤条件]
[ORDER BY 排序列表]
[LIMIT 开始⾏, 限制条数]
1) : 查询的操作符 (where + 查询条件)
-
操作符
⽐较操作符可以组成搜索条件,满⾜搜索条件的记录将会被放⼊结果集中 -
简单搜索条件
① select 列名 from 表名 where 搜索条件;
搜索条件 比如: name = '范剑'
② 查询一个列,在什么和什么之间
select * from 表名 where 类名 between b and c;
③ 查询不在什么什么之间
select * from student_info where number not between 1 and 5;
-
匹配列表中的元素
select * from student_info where number in (1,3,9); -- 输出学号1,3,9存在的学生信息
-
匹配 NULL 值
select name from student_info where department is null;
查询学院信息为空的学生有哪些
select name from student_info where department is not null;
查询学院信息不为空的学生有哪些
-
多个搜索条件的查询
① and操作符
获取满足两个条件的信息
select * from student_score where (subject = '语文') and (score > 85);
② or操作符
获取满足一个条件的信息
select * from student_info where (department = '软件工程学院') or (department = '大数据');
-
通配符 ① % 代表0个或者多个任意字符 ② _ 代表一个字符
模糊查询的关键字 like
select * from student_info where name like '李%'; select * from student_info where name not like '李_'; select * from student_info where name like '李__';
-
转义通配符
如果匹配字符串中需要普通字符 '%' 或者 '_' 的话,需要在它们前边加⼀个反斜杠 \ 来和通配符区分开
'\%' 代表普通字符 '%'
'斜杠_' 代表普通字符 '_'
2) : 分组查询
-
分组创建 group by(列名) select sex, count(sex) from student_info group by(sex);
-
带有WHERE⼦句的分组查询 where 查询条件 group by 列名
select subject,avg(score) from student_score where score >= 60 group by subject;
① 将记录过滤掉不符合 WHERE ⼦句的记录后分组。② 分别对各个分组进⾏数据统计
-
作用于分组的过滤条件 having
select subject,avg(score) from student_score where score >= 60 group by subject having avg(score) > 80;
-
分组和排序
select subject,avg(score) as avg_score from student_score where score >= 60 group by subject having avg(score) > 60 order by avg_score desc limit 1;
-
嵌套分组
select department,major,count(*) from student_info group by department,major;
-
使⽤分组注意事项
① 如果分组列中含有 NULL 值,那么 NULL 也会作为⼀个独⽴的分组存在。
② 如果存在多个分组列,也就是 嵌套分组 ,聚集函数将作⽤在最后的那个分组列上。
③ 如果查询语句中存在 WHERE ⼦句和 ORDER BY ⼦句,那么 GROUP BY ⼦句必须出现在 WHERE⼦句之后, ORDER BY ⼦句之前。
④ ⾮分组列 不能单独出现在检索列表中(可以被放到聚集函数中)。
⑤ GROUP BY ⼦句后也可以跟随 表达式 (但不能是聚集函数)。
⑥ WHERE ⼦句和 HAVING ⼦句的区别。
WHERE ⼦句在分组前进⾏过滤,作⽤于每⼀条记录, WHERE ⼦句过滤掉的记录将不包括在分组中。
⽽ HAVING ⼦句在数据分组后进⾏过滤,作⽤于整个分组。
3) : 子查询 (where条件里边的)
-
标量⼦查询 number只能有一个值
第⼆条查询语句⽤⼩括号 () 扩起来作为⼀个操作数放到了第⼀条的搜索条件处,这样就起到了合并两条查询语句的作⽤。⼩括号中的查询语句也被称为 ⼦查询 或者 内层查询 ,使⽤内层查询的结果作为搜索条件的操作数的查询称为 外层查询 。
select * from student_score where number = (select number from student_info where name = '李四');
如果名字有重复的,那么就会报错,需要使用 in 操作符
-
列⼦查询 (常用) 列⼦查询得到的结果是多个值 ,相当于⼀个列表
select * from student_score where number in (select number from student_info where name = '李四');
第二条查询语句的结果集中该列包含2个值,一个名字对应两个学号; number in (2,7);
IN 和 NOT IN 操作符正好是⽤来匹配列表的,上边使⽤的例⼦是使⽤ IN 操作符和⼦查询的结果组成表达式来作为外层查询的搜索条件的。
-
行子查询
只要⼦查询的结果集中最多只包含⼀条记录,⽽且这条记录中有超过⼀个列的数据(如果该条记录只包含⼀个列的话,该⼦查询就成了 标量⼦查询 ),那么这个⼦查询就可以被称之为 ⾏⼦查询
-
表⼦查询
如果⼦查询结果集中包含多⾏多列,那么这个⼦查询也可以被称之为 表⼦查询
select * from student_score where(number, subject) in (select number, '语文' from student_info where name = '李四');
-
EXISTS和NOT EXISTS⼦查询
-
相关子查询
需要在⼦查询的语句中引⽤到外层查询的值,这样的话⼦查询就不能当作⼀个独⽴的语句去执⾏,这种⼦查询被称为相关⼦查询 。
select number,name,id_number,major from student_info as s where exists (select * from student_score as t where s.number = t.number);
4) : 连接查询
-
连接的概念
把各个表中的记录都取出来依次匹配的组合加⼊结果集并返回给⽤户。
连接查询的结果集中包含⼀个表中的每⼀条记录与另⼀个表中的每⼀条记录相互匹配的组合,像这样的结果集就可以称之为 笛卡尔积。(笛卡尔积: 表中的n条记录*另一张表的n条记录)
-
连接查询
连接查询的语法也很随意,只要在 FROM 语句后边跟多个⽤逗号 , 隔开的表名就好了
select * from t1, t2;
-
内连接和外连接 on
对于 内连接 的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加⼊到
最后的结果集,我们上边提到的连接都是所谓的 内连接 。
对于 外连接 的两个表,驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加⼊到结果集。
涉及单表的过滤条件放到 WHERE ⼦句中,把涉及两表的过滤条件都放到 ON ⼦句中,我们也⼀般把放到 ON ⼦句中的过滤条件也称之为 连接条件。内连接中的WHERE⼦句和ON⼦句是等价的。
① 左(外)连接的语法 left join
SELECT * FROM t1 LEFT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
select * from student_info s left join student_score t on s.number = t.number;
② 右(外)连接的语法 right join
SELECT * FROM t1 RIGHT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
select * from student_score s right join student_info t on s.number = t.number;
③ 内连接的语法 inner join
由于在内连接中ON⼦句和WHERE⼦句是等价的,所以内连接中不要求强制写明ON⼦句
SELECT * FROM t1 [INNER | CROSS] JOIN t2 [ON 连接条件] [WHERE 普通过滤条件];
select distinct s.* from student_info s inner join student_score t on s.number = t.number;
④ 多表连接
一种写法: select * from t1 inner join t2 inner join t3 on t1.m1 = t2.m2 and t1.m1 = t3.m3;
二种写法:(常用) select * from t1
inner join t2 on t1.m1 = t2.m2 inner join t3 on t1.m1 = t3.m3;
⑤ 自连接
同⼀个表也可以进⾏连接。⽐⽅说我们可以对两个 t1 表 (为了把两个⼀样的表区分⼀下,需要为表定义别名) 来连接去查询
select t.number, t.name, t.major from student_info as s inner join student_info as t on s.major = t.major and s.name = '李四';
简单理解: 一张表定义了两个名,看做成两个表,拿其中一个去查找另一个中的列信息。
这个⼦查询就可以与上边的连接查询替换,两者运行结果都如下图所示:
select number,name,major from student_info where major in(select major from student_info where name = '李四');
5) : 组合查询 union
使⽤ UNION 来合并多个查询的记录会默认 过滤掉重复 的记录
union all 不去重
示例:
select number from student_info union all select number from student_score union all select 8 from dual order by number limit 4;
这⼏个查询语句的结果集都可以被合并到⼀个⼤的结果集中,但是这个⼤的结果集总是要有展示⼀下列名的吧,所以就规定组合查询的结果集中显示的列名将以第⼀个查询中的列名为准。
由于最后的结果集展示的列名是第⼀个查询中给定的列名,所以 ORDER BY ⼦句中指定的排序列也必须是第⼀个查询中给定的列名(别名也可以)。
四: 插入语句 insert into
-
插⼊数据:
insert into 表名(列1, 列2, ...) values (列1的值,列2的值, ...);
-
批量插入数据:
insert into 表名(列1, 列2, ...) values (列1的值,列2的值, ...), (列1的值,列2的值, ...),...;
-
将某个查询的结果集插⼊表中:
insert into t1 select m2,n2 from t2;
-
将多个查询的结果集插⼊表中:
insert into t1 select m2,n2 from t2 union all select m3,n3 from t3;
-
insert ignore 忽略 具有约束并且重复 的插入
对于那些是主键或者具有UNIQUE约束的列或者列组合来说,如果表中已存在的记录中没有与待插⼊记录在这些列或者列组合上重复的值,那么就把待插⼊记录插到表中,否则忽略此次插⼊操作。
insert ignore into t1 select m1,n1 from t1_bak;
-
INSERT ... ON DUPLICATE KEY UPDATE ... 几乎不用
对于那些是主键或者具有UNIQUE约束的列或者列组合来说,如果表中已存在的记录中没有与待插⼊记录在这些列或者列组合上重复的值,那么就把待插⼊记录插到表中,否则按照规定去更新那条重复的记录中某些列的值。
五: 更新数据
update 表名 set 列1=值1, 列2=值2, ..., 列n=值n [where 布尔表达式];
例: update t1 set n1 = 'x' where m1 = 3;
六: 视图 其实就相当于是某个查询语句的别名
-
-- 创建视图 create view 视图名 as 查询语句 create view v_student_info as select number, name from student_info;
-
-- 删除视图 drop view 视图名 drop view v_student_info;
-
-- 内嵌视图 select s.* from (select number, name from student_info) as s [where条件];
七: 列的属性 主键外键,unique约束 索引
-
默认值
列名 列的类型 default 默认值
-
not null 属性:
列名 列的类型 NOT NULL
-
主键约束 (默认不为空,不重复)
① primary key(列名)
② primary key (列名1, 列名2, ...)
create table student_score (
number int,
subject varchar(30),
score tinyint,
primary key(number, subject)
);
-
unique 约束属性 索引index
每当我们为某个列添加了⼀个 UNIQUE 属性,该列的值就不能重复
① unique [约束名称] (列名1, 列名2, ...)
② unique key [约束名称] (列名1, 列名2, ...)
-- 加入索引 为了提高查询速度 alter table t1 add unique key unique_1(m1); -- 删除索引 alter table t1 drop index unique_1;
什么是索引? 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
-
主键和 unique 约束的区别:
主键和 unique 约束都能保证某个列或者列组合的唯⼀性,但是:
① ⼀张表中只能定义⼀个主键,却可以定义多个 unique 约束!
② 规定:主键列不允许存放null ,⽽声明了 unique 属性的列可以存放 null ,⽽且 null 可以重复地出现在多条记录中!
-
外键约束 (后续添加外键)
constraint [外键名称] foreign key (列1, 列2, ...) references ⽗表名(⽗列1, ⽗列2, ...);
例子: create table student_score (
number int,
subject varchar(30),
score tinyint,
primary key (number, subject),
constraint foreign key(number) references student_info(number)
);
-- 如果A表中的某个列或者某些列依赖与B表中的某个列或者某些列.那么就称A表为⼦表 ,B表为⽗表 。
上边例⼦中⼦表和⽗表可以使⽤外键来关联起来, 成绩表的学号列依赖于信息表的学号列,所以信息表就是⼀个⽗表, 成绩表就是⼦表。
这样,在对成绩表插⼊数据的时候, MySQL 都会为我们检查⼀下插⼊的学号是否能在 信息表中找到,如果找不到则会报错。
❤❤❤❤❤❤创建完表之后,再添加外键❤❤❤❤❤❤❤❤
-- 先创建表 create table if not exists ums_client_user_cost( id varchar(36) primary key comment'主键', client_user_id varchar(36) comment'用户编号', initial_value bigint comment'初始值', initial_time date comment'初始时间', current_value int comment'当前值', current_time1 date comment'当前时间' ) comment'用户费用表'; -- 后续添加外键 alter table ums_client_user_cost add constraint fk_UMS_CLIENT_USER_COST_UMS_CLIENT_USER_1 foreign key(client_user_id) references ums_client_user(id);
❤❤❤❤❤❤创建完表之后,再添加外键❤❤❤❤❤❤❤❤
-
auto_increment 自增属性 (一个表中只能有一个列具有这个属性)
列名 列的类型 auto_increment
-
列的注释
每⼀个列末尾添加 comment 语句来为列来添加注释
-
影响展示外观的 zerofill 补零属性
CREATE TABLE zerofill_table (
i1 INT ZEROFILL,
i2 INT unsigned
);
给该列加⼀个 ZEROFILL 属性(也可以理解为这是⼀个属于数据类型的属性)
在创建表的时候,如果声明了 ZEROFILL 属性的列没有声明 UNSIGNED 属性,那 MySQL 会为该 列⾃动⽣成 UNSIGNED 属性。
八: ⾃定义变量
-
-- 自定义变量 set @a = 1;
-
-- 查询变量
select @a;
-
如果我们的查询结果是⼀条记录,该记录中有多个列的值的话,我们想把这⼏个值分别赋值到不同的变量中,只能使⽤ into 语句了:
SELECT m1, n1 FROM t1 LIMIT 1 into @a, @b;
这条查询语句的结果集中只包含⼀条记录,我们把这条记录的 m1 列的值赋值到了变量 a 中, n1列的值赋值到了变量 b 中。
九: 存储过程和函数
1) 存储函数
-
创建存储函数
存储函数 其实就是⼀种 函数 ,只不过在这个函数⾥可以执⾏ MySQL 的语句⽽已。
create function 存储函数名称 ([参数列表])
returns 返回值类型
begin
[declare 局部变量 类型 默认值;]
函数体内容
return 返回值;
end
函数体内容可以包括⼀条或多条语句,每条语句都要以分号 ; 结尾。
-
存储函数的调⽤
select 函数名称 ([参数列表]);
函数调⽤可以放到查询列表或者作为搜索条件,或者和别的操作数⼀起组成更复杂的表达式
-
查看和删除存储函数
① 查看存储函数
show create function 函数名
② 删除函数
drop function 函数名
-
在函数体中定义局部变量
在存储函数的函数体中使⽤变量前必须先声明这个变量(在存储函数的函数体中,declare语句必须放到其他语句的前边)
declare 变量名1, 变量名2, ... 数据类型 [default默认值];
例子: -- 函数体中声明变量 create function my_now() returns varchar(30) begin declare d, t varchar(20); set d = (select curdate() from dual); set t = (select curtime() from dual);
return concat(d, ' ', t);
end -- 函数调用 select my_now();
-
在函数体中使⽤⾃定义变量
-- 创建函数 在函数体中使用自定义变量 create function user_defined_var_demo() returns int begin set @abc = 10; return @abc; end;
-- 调用函数或者直接查询自定义的变量
select user_defined_var_demo(); select @abc;
虽然现在存储函数执⾏完了,但是由于在该函数执⾏过程中为⾃定义变量 abc 赋值了,那么在该函数执⾏完之后我们仍然可以访问到该⾃定义变量的值
-
判断语句的编写
IF 表达式 THEN
处理语句列表
[ELSEIF 表达式 THEN
处理语句列表]
... # 这⾥可以有多个ELSEIF语句
[ELSE
处理语句列表]
END IF;
其中 处理语句列表 中可以包含多条语句,每条语句以分号 ; 结尾就好。
例子:
-- 判断语句 create function getGrade(num int) returns varchar(20) begin declare avg_score double default 0.0; declare result varchar(20); set avg_score = (select avg(score) from student_score where number = num); f avg_score >= 80 then set result = '优秀'; elseif avg_score >= 60 then set result = '及格'; elseif avg_score < 60 then set result = '不及格'; elseif avg_score is null then set result = '异常'; end if;
return result;
end -- 查询语句 select avg(score) from student_score where number = 1; -- 函数调用 select getGrade(2) from dual;
-
while循环语句:
WHILE 表达式 DO
处理语句列表
END WHILE;
-
repeat语句和while语句的区别:
repeat 循环语句,先执⾏处理语句,再判断 表达式 是否成⽴,如果成⽴则退出循环,否则继续执⾏处理语句,所以⾄少执⾏⼀次处理语句。
while 循环语句,先判断表达式的值,再执⾏处理语句。
-
repeat循环语句
REPEAT
处理语句列表
UNTIL 表达式 END REPEAT;
-
loop 循环语句
LOOP
处理语句列表
END LOOP;
例子: 查询1-n 的数字总和
-- loop 循环函数
create function my_sum(n int unsigned)
returns int
begin
declare i int default 0;
declare result int default 0;
loop
if i > n then
return result;
end if;
set result = result + i;
set i = i + 1;
end loop;
end
-- 调用函数
select my_sum(10);
2) 存储过程
-
创建存储过程
create procedure 存储过程名称([参数列表])
begin
需要执⾏的语句
end
-
存储过程的调用
call 存储过程([参数列表]);
-
存储过程的参数前缀
参数类型 [IN | OUT | INOUT] 参数名 数据类型
OUT参数的值默认为 NULL
案例: 求数学成绩最大值,最小值,平均值
3) 存储函数和存储过程区别
存储函数 和 存储过程 都属于 存储例程 ,都是对某些语句的⼀个封装。
存储过程 与 存储函数 最直观的不同点就是, 存储过程 的定义不需要声明 返回值类型 。
-
存储函数在定义时需要显式⽤ RETURNS 语句标明返回的数据类型,⽽且在函数体中必须使
⽤ RETURN 语句来显式指定返回的值,存储过程不需要。
-
存储函数只⽀持 IN 参数,⽽存储过程⽀持 IN 参数、 OUT 参数、和 INOUT 参数。
-
存储函数只能返回⼀个值,⽽存储过程可以通过设置多个 OUT 参数或者 INOUT 参数来返回多个结果。
-
存储函数执⾏过程中产⽣的结果集并不会被显示到客户端,⽽存储过程执⾏过程中产⽣的结果集会被显示到客户端。
-
存储函数直接在表达式中调⽤,⽽存储过程只能通过 CALL 语句来显式调⽤。
十: 游标的使用,和function、procedure 一起使用
1) 游标
-
游标简介
游标 为了⽅便我们去访问这些有多条记录的结果集,其实就是⽤来标记结果集中我们正在访问的某⼀条记录。
游标不能单独用,一般和function procedure 一起使用
-
游标的使用方式,分为四个步骤及内部具体步骤:
create procedure 存储过程名称([参数列表])
begin
定义变量
定义循环未结束时的变量
① 定义游标 并对表格进行行转列
declare 游标名称 cursor for 查询语句;
存储程序有声明局部变量的语句,创建游标的语句⼀定要放在局部变量声明后头
1.5步: 定义遍历结束时的执⾏策略
declare continue handler for not found set no_data = 0;
② 打开游标
open 游标名称;
③ 通过游标访问记录
循环语句
fetch 游标名 into 变量1, 变量2, ... 变量n;
循环语句结束;
④ 关闭游标
close 游标名称;
end;
call 存储过程名称();
-
案例 获取每个学生的分数总和 while循环
-- 获取每个学生的分数总和 while循环
create procedure p_get_numberSum()
begin
-- 定义变量 学号 语文 数学成绩
declare num int;
declare c_score double default 0.0;
declare m_score double default 0.0;
-- 定义循环遍历未结束时的变量
declare no_data int default 1;
-- 定义游标 对student_score进行 行转列
declare cursor_student_score cursor for
select number,
ifnull(sum(case subject when '语文' then score end),0) as Chinese,
ifnull(sum(case subject when '数学' then score end),0) as Maths
from student_score group by number;
-- 将循环遍历结束时的变量 设置no_data = 0
declare continue handler for not found set no_data = 0;
-- 打开游标
open cursor_student_score;
-- 通过游标访问记录 循环语句 while
while no_data = 1 do
fetch cursor_student_score into num, c_score, m_score;
if no_data = 1 then
select num as number, c_score + m_score as sum_score
from dual;
end if;
end while;
-- 循环语句 loop
-- flag: loop
-- fetch cursor_student_score into num, c_score, m_score;
-- if no_data = 0 then
-- leave flag;
-- end if;
-- select num as number, c_score + m_score as sum_score from dual;
-- end loop flag;
-- 关闭游标
close cursor_student_score;
end;
-- 调用存储过程
call p_get_numberSum();
2) 行转列![](https://i-blog.csdnimg.cn/blog_migrate/962512a5d962e426eca3c787959acad2.png)
-- 行转列 select number, (case subject when '语文' then score end) as Chinese, (case subject when '数学' then score end) as Maths from student_score;
-- 按学号汇总行 select number, sum(case subject when '语文' then score end) as Chinese, sum(case subject when '数学' then score end) as Maths from student_score group by number;
-- 处理数据:将空值的栏位填入数字0 select number, ifnull(sum(case subject when '语文' then score end) ,0) as Chinese, ifnull(sum(case subject when '数学' then score end) ,0) as Maths from student_score group by number;
十一: 触发器和事件
1) 触发器
-
创建触发器的格式 create trigger 触发器名称 {before|after} {insert|delete|update} on 表名 for each row begin 触发器内容 end;
-
-
MySQL 提供了 NEW 和 OLD 两个单词来分别代表新记录和旧记录
对于 INSERT 语句设置的触发器来说, NEW 代表准备插⼊的记录, OLD ⽆效。
对于 DELETE 语句设置的触发器来说, OLD 代表删除前的记录, NEW ⽆效。
对于 UPDATE 语句设置的触发器来说, NEW 代表修改后的记录, OLD 代表修改前的记录。
-
查看和删除触发器
SHOW CREATE TRIGGER 触发器名;
-
删除触发器:
DROP TRIGGER 触发器名;
-
注意事项:
触发器内容中不能有输出结果集的语句。
触发器内容中 NEW代表记录的列的值可以被更改,OLD代表记录的列的值⽆法更改。
在before触发器中,我们可以使⽤ set new.列名 = 某个值 的形式来更改待插⼊记录或者待更新记录的某个列的值,但是这种操作不能在after触发器中使⽤,因为在执⾏AFTER触发器的内容时记录已经被插⼊完成或者更新完成了。
如果我们的 before 触发器内容执⾏过程中遇到了错误,那这个触发器对应的具体语句将⽆法执⾏;如果具体的操作语句执⾏过程中遇到了错误,那与它对应的 after 触发器的内容将⽆法执⾏。
2) 事件
-
概念
有时候我们想让 MySQL 服务器在某个时间点或者每隔⼀段时间⾃动地执⾏⼀些语句,这时候就需要去创建⼀个 事件 。
-
创建事件格式
create event 事件名 on schedule { at 某个确定的时间点 | every 期望的时间间隔 [starts datetime] [end datetime] } do 具体的语句;
-
创建-单次定时执行事件
-
示例 1:往demo_1119表中插入一行数据,执行时间:2020-11-20 00:00:00
create event demo_event2 on schedule at timestamp '2020-11-20 00:00:00' do insert into `demo_1119` (`id`, `name`, `createTime`) values (null, '陈哈哈', NOW())
-
示例 2:往demo_1119表中插入一行数据,执行时间:当前时间往后5个小时;
CREATE EVENT demo_event2 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 5 HOUR DO INSERT INTO `demo_1119` (`id`, `name`, `createTime`) VALUES (null, '陈哈哈', NOW())
-
-
创建-循环定时执行事件
-
示例 1:从当前开始,每10秒往demo_1119表中插入一行数据
create event demo_event3 on schedule every 10 SECOND do insert into `demo_1119` (`id`, `name`, `createTime`) VALUES (null, '陈哈哈', NOW())
-
示例 2:从当前时间一小时后开始,每10分钟往demo_1119表中插入一行数据
CREATE EVENT demo_event5 ON SCHEDULE EVERY 10 MINUTE STARTS CURRENT_TIMESTAMP+INTERVAL 1 HOUR DO INSERT INTO `demo_1119` (`id`, `name`, `createTime`) VALUES (null, '陈哈哈', NOW())
-
-
查看和删除事件
show create event 事件名;
drop event 事件名;
十二: 数据库事务
MySQL中只有innodb存储引擎支持事务
-
定义:
数据库事务是构成单一逻辑工作单元的操作(insert、delete、update)集合。
-
事务的ACID特性
原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)
-
MySQL开始手动提交事务
start transaction; DML语句 commit;
-
MySQL开始手动回滚事务
start transaction; DML语句 rollback;
-
数据库隔离级别与并发异常
什么是脏读?幻读?不可重复读? 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
什么是事务的隔离级别?MySQL的默认隔离级别是什么? 为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
SQL 标准定义了四个隔离级别: READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。 READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。 REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。 SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。 这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle默认采用的READ_COMMITTED隔离级别事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。 InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。
-
MySQL数据库中事务隔离级别
查看隔离级别: select @@transaction_isolation (mysql版本 8.0 以后) select @@tx_isolation (mysql版本 8.0 之前)