MySQL之高级语句(二)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⠉⠛⣿⣿⡄
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣍⣀⣈⣙⣿⣿⣿⣿⡿⠟⣀⣠⣤⣄⡄⠻⣿⣇
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠄⣿⡿⠛⠛⠛⢻⣿⣿⣧⣾⣏⣀⣀⠀⢁⣠⣼⣿
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣿⣿⣿⡧
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⢿⣿⣿⣿⣟⣙⢻⣿⣿⣿⡿⠿⠗
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠏⣼⠿⣿⠿⠟⠋⠀⠘⣿⠏⠀⠀⠀⢀⠀⠁
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣴⣿⣶⣷⣶⣶⣶⡆⢠⣿⣷⣾⡟⠀⠈
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⠛⠉⠁⠀⠀⠀⠀⠿⣿⠟⠁
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣷⣦⣽⣓⡠⠝⠀⠠⠘⠋
⠀⠀⠀⠀⠀⠀⠀⣠⣤⡀⠀⠀⠀⢹⣿⣿⣿⣤⣍⣀⣀⣀⡴⠁⠀⠀⠀⢀\
⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⠀⠀⠀⠀⠙⢄⠙⠛⠛⠉⠀⠈\
⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⠀⠀⠀⠀⠀⠀⠈⠐⠠⠤\
⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣀⡀⠀⠀⣀⣀⡀\
⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡄\
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷\
⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣴⣿⣿⣟⡉⡁\
⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠏\
⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠥⡆\
视图(CREATE VIEW)
CREATE VIEW——视图,可以被当作是虚拟表或存储查询。
- 视图跟表格的不同是,表格中有实际储存数据记录,而视图是建立在表格之上的一个架构,它本身并不实际储存数据记录。
- 临时表在用户退出或同数据库的连接断开后就自动消失了,而视图不会消失。
- 视图不含有数据,只存储它的定义,它的用途一般可以简化复杂的查询。 比如你要对几个表进行连接查询,而且还要进行统计排序等操作,写SQL语句会很麻烦的,用视图将几个表联结起来,然后对这个视图进行查询操作,就和对一个表查询一样,很方便。 视图表就是保存了select语句的查询结果,原表数据变化后,视图表的结果也会发生变化。
语法:
CREATE VIEW "视图表名" AS "SELECT 语句"; #创建视图表
DROP VIEW "视图表名"; #删除视图表
示例:
#将两个表的连接查询,创建为视图表
create view v_region_sales as select A.region region,sum(B.sales) sales from location A inner join store_info B on A.store_name=B.store_name group by region;
select * from v_region_sales; #查看视图表的存储结果
drop view v_region_sales; #删除视图表
视图表与派生表
视图表,简化复杂的查询。只需创建一次,后面可以直接对已创建好的视图表进行操作。
派生表,两个select查询写在一个句子当中,比较复杂冗长。
#视图表:只需创建一次,后面可以直接对已创建好的视图表进行操作。
create view v_region_sales as select A.region region,sum(B.sales) sales from location A inner join store_info B on A.store_name=B.store_name group by region;
select sum(sales) from v_region_sales; #对视图表进行操作
#派生表:C就是子查询中select语句的派生表。
select sum(C.sales) from (select A.region region,sum(B.sales) sales from location A,store_info B where A.store_name=B.store_name group by region) C;
视图表插入数据
-
如果视图表的结构和原表结构一致,可以修改和插入数据。
- 例如原表有3个字段,视图表有2个字段,只要这2个字段和原表结构是一致的,也可以修改和插入数据。(只要该视图表是对单个表的查询结果即可)
-
如果视图表是两个表的连接查询,则无法插入数据。因为表结构和原表不一致。
示例:
如果视图表的结构和原表结构一致,可以修改和插入数据。(只要该视图表是对单个表的查询结果即可)
#该视图表只是对单个表的查询结果。
create view v_test_info as select * from store_info;
#查看视图表结构,和原表一致。
desc v_test_info;
#插入数据成功
insert into v_test_info values('NYC',500,'2020-12-10');
如果视图表是两个表的连接查询,则无法插入数据。因为表结构和原表不一致
#该视图表是两个表的连接查询
create view v_region_sales as select A.region region,sum(B.sales) sales from location A inner join store_info B on A.store_name=B.store_name group by region;
#查看视图表结构,和原表不一致。
desc v_region_sales;
#插入数据失败
insert into v_region_sales values('middle',500);
联集
UNION联集:将两个SQL语句的结果合并起来,两个SQL语句所产生的字段需要是同样的数据记录种类。
UNION(合并后去重)
生成结果的数据记录值将没有重复,且按照字段的顺序进行排序。
[select 语句1] UNION [select 语句2];
示例:
select store_name from location union select store_name from store_info;
#将两个表的store_name字段的值合并起来,union生成的结果去重
UNION ALL(合并后不去重)
将生成结果的数据记录值都列出来,无论有无重复。
[select 语句1] UNION ALL [SELECT 语句2];
示例:
select store_name from location union all select store_name from store_info;
#将两个表的store_name字段的值合并,全都列出来,union all生成的结果不去重。
交集值
交集值:取两个SQL语句结果的交集。
1 使用union all + group by+having求交集值
注意:使用联集的方式求交集时,两个表的目标字段值必须先各自去重,之后再合并。避免因为单个表内存在重复值,导致错误计算。
#两个表各自将store_name字段的值进行去重,之后合并,再创建视图表。
create view v_store_name as select distinct store_name from location union all select distinct store_name from store_info;
#对视图表的store_name字段进行分组汇总,计算每组的数量。
select store_name,count(*) from v_store_name group by store_name;
#对视图表的store_name字段进行分组汇总,计算每组的数量,过滤出数量大于1的store_name字段值,就是两个表的交集部分。
select store_name from v_store_name group by store_name having count(*) >1;
2 使用内连接求交集值
取两个表的store_name字段值的交集部分:
select A.store_name from location A inner join store_info B on A.store_name=B.store_name;
select A.store_name from location A inner join store_info B using(store_name);
取两个表的store_name字段值的交集部分,之后去重,加distinct:
select distinct A.store_name from location A inner join store_info B using(store_name);
3 使用左连接求交集值
#使用左连接查询store_name字段的交集部分
select * from location A left join store_info B using(store_name);
#使用左连接查出store_name字段的交集值,之后去重
select distinct A.store_name from location A left join store_info B using(store_name) where B.store_name is not null;
4 使用右连接求交集值
#使用右连接查出store_name字段的交集值,之后去重
select distinct A.store_name from location A right join store_info B using(store_name) where A.store_name is not null;
#方法二:
select distinct A.store_name from location A right join store_info B on A.store_name=B.store_name where A.store_name is not null;
5 使用子查询的方式求交集值 in
#使用子查询的方式查出store_name字段的交集值,之后去重
select distinct store_name from location where store_name in (select store_name from store_info);
无交集值
无交集值:显示第一个SQL语句的结果,且与第二个SQL语句没有交集的结果,且没有重复。
使用union all + group by+having求无交集值:
#两个表各自将store_name字段的值进行去重,之后合并,再创建视图表。
create view v_store_name as select distinct store_name from location union all select distinct store_name from store_info;
#对视图表的store_name字段进行分组汇总,计算每组的数量。
select store_name,count(*) from v_store_name group by store_name;
#对视图表的store_name字段进行分组汇总,计算每组的数量,过滤出数量等于1的store_name字段值,就是两个表无交集的部分。
select store_name from v_store_name group by store_name having count(*) =1;
CASE
case:是 SQL 用来作为 IF-THEN-ELSE 之类逻辑的关键字。 语法:
SELECT CASE ("字段 名")
WHEN "条件1" THEN "结果1"
WHEN "条件2" THEN "结果2"
[ELSE "结果N"]
END
FROM "表名";
# "条件"可以是一个数值或是公式。ELSE子句则并不是必须的。
示例:
select store_name,CASE store_name #对sttore_name字段值进行判断
WHEN 'Los Angeles' THEN sales * 2 #当店名为洛杉矶时销售额乘2
WHEN 'Boston' THEN 2000 #当店名为波士顿时销售额显示2000
ELSE sales / 2 #其他店名则销售额除以2
END #判断结束
"new sales", date #用于显示的字段名
from store_info;
#"new sales" 是用于case那个字段的字段名。
#注:查询并不改变原表的值。
空值(NULL)和无值(' ')的区别
空值(NULL)和无值(' ')的区别:
- 无值的长度为0,不占用空间;而NULL值的长度是NULL,是占用空间的。
IS NULL或者IS NOT NULL,是用来判断字段是不是为NULL或者不是NULL,不能查出是不是无值的。
- 无值的判断使用
=' '或者< >' '来处理。<>代表不等于。
- 在通过
count ()指定字段统计有多少行数时,如果遇到NULL值会自动忽略掉,遇到无值会加入到记录中进行计算。 先准备一个表city:
create table city(name varchar(10));
insert into city values('beijing');
insert into city values('nanjing');
insert into city values();
insert into city values();
insert into city values('hangzhou');
insert into city values('');
示例:
- 查看无值和空值的长度。 无值的长度为0,不占用空间;而NULL值的长度是NULL,是占用空间的。
select length(NULL), length(''), length('123');
IS NULL或者IS NOT NULL,是用来判断字段是不是为NULL或者不是NULL,不能查出是不是无值的。
select * from city where name is null;
#查询字段值为NULL的数据记录
select * from city where name is not null;
#查询字段值不为NULL的数据记录
- 无值的判断使用
=' '或者< >' '来处理。< > 代表不等于。
select * from city where name='';
#查询字段值为无值的数据记录
select * from city where name <> '';
#查询字段值不为NULL以及不为无值的数据记录
- 在通过
count ()指定字段统计有多少行数时,如果遇到NULL值会自动忽略掉,遇到无值会加入到记录中进行计算。
select count(name) from city;
#会忽略null值,但不会忽略无值
select count(*) from city;
#会统计所有行
正则表达式——精确查询
| 匹配模式 | 描述 | 实例 |
|---|---|---|
| ^ | 匹配文本的开始字符 | ‘^bd’ 匹配以 bd 开头的字符串 |
| $ | 匹配文本的结束字符 | ‘qn$’ 匹配以 qn 结尾的字符串 |
| . | 匹配任何单个字符 | ‘s.t’ 匹配任何 s 和 t 之间有一个字符的字符串 |
| * | 匹配零个或多个在它前面的字符 | ‘fo*t’ 匹配 t 前面有任意个 o |
| + | 匹配前面的字符 1 次或多次 | ‘hom+’ 匹配以 ho 开头,后面至少一个m 的字符串 |
| 字符串 | 匹配包含指定的字符串 | ‘clo’ 匹配含有 clo 的字符串 |
| p1 | p2 | 匹配 p1 或 p2 |
| [...] | 匹配字符集合中的任意一个字符 | ‘[abc]’ 匹配 a 或者 b 或者 c |
| [^...] | 匹配不在括号中的任何字符 | '[abc]' 匹配不包含 a 或者 b 的字符串 |
| {n} | 匹配前面的字符串 n 次 | ‘g{2}’ 匹配含有 2 个 g 的字符串 |
| {n,m} | 匹配前面的字符串至少 n 次,至多m 次 | ‘f{1,3}’ 匹配 f 最少 1 次,最多 3 次 |
语法:
select "字段" from "表名" where "字段" regexp {模式};
示例:
#查询store_name字段包含字符串"os"的行
select * from store_info where store_name regexp 'os';
#查询store_name字段以A-G开头的行
select * from store_info where store_name regexp '^[A-G]';
#包含Ho或者Bo
select * from store_info where store_name regexp 'Ho|Bo';
#以“Bo”开头且以“on”为结尾
select * from store_info where store_name regexp '^Bo.*on$';
存储过程
存储过程是什么
存储过程是一组为了完成特定功能的SQL语句集合。
存储过程在使用过程中是将常用或者复杂的工作预先使用SQL语句写好并用一个指定的名称存储起来,这个过程经编译和优化后存储在数据库服务器中。当需要使用该存储过程时,只需要调用它即可。存储过程在执行上比传统SQL速度更快、执行效率更高。
优点
- 执行一次后,会将生成的二进制代码驻留缓冲区,提高执行效率
- SQL语句加上控制语句的集合,灵活性高\
- 在服务器端存储,客户端调用时,降低网络负载\
- 可多次重复被调用,可随时修改,不影响客户端调用\
- 可完成所有的数据库操作,也可控制数据库的信息访问权限
创建、调用、查看、删除存储过程
##创建存储过程:
DELIMITER $$ #将语句的结束符号从分号;临时改为两个$$ (可以是自定义)
CREATE PROCEDURE Proc () #创建存储过程,过程名为Proc,不带参数
-> BEGIN #过程体以关键字BEGIN开始
-> select * from store_info; #过程体语句
-> END $$ #过程体以关键字END结束
DELIMITER; #将语句的结束符号恢复为分号
##调用存储过程##
CALL Proc;
##查看存储过程##
SHOW CREATE PROCEDURE [数据库.]存储过程名; #查看某个存储过程的具体信息
SHOW CREATE PROCEDURE Proc;
SHOW CREATE PROCEDURE Proc\G
SHOW PROCEDURE STATUS [LIKE '%Proc%'] \G
##删除存储过程##
#存储过程内容的修改方法是通过删除原有存储过程,之后再以相同的名称创建新的存储过程。
DROP PROCEDURE IF EXISTS Proc;
#仅当存在时删除,不添加If EXISTS 时,如果指定的过程不存在,则产生一个错误。
示例:
- 创建和调用
delimiter $$
create procedure proc01 () #创建存储过程,过程名为proc01,不带参数
-> begin #过程体以关键字BEGIN开始
-> create table student(id int,name char(10),age int);
-> insert into student values(1,'huge',25);
-> insert into student values(2,'dingzhen',20);
-> select * from student;
-> end $$ #过程体以关键字END结束
delimiter ; #将语句的结束符号恢复为分号
call proc01; #调用存储过程
- 查看存储过程
show create procedure proc01;
show create procedure proc01\G
#查看存储过程的具体信息
show procedure status like '%Proc01%'\G
3. 删除存储过程
drop procedure if exists proc01;
#仅当存在时删除,不添加If EXISTS 时,如果指定的过程不存在,则产生一个错误。
存储过程的参数
- IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
- OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
- INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
delimiter $$
create procedure proc2(in stuname char(20)) #参数为stuname,数据类型一定要与下面的where语句后字段的数据类型相同
-> begin
-> select * from student where name = stuname;
-> end $$
delimiter ;
call proc2('dingzhen'); #调用存储过程,并传入参数'dingzhen'
注:变量名不能有下划线
存储过程的控制语句
#创建一个表格
create table abc (id int(10)) ;
insert into abc values (10) ;
条件语句 if-then-else . .. . end if
delimiter $$
create procedure proc03(in innum int) #创建存储过程proc03,参数为innum,类型为int
-> begin
-> declare var int; #定义变量var为int类型
-> set var=innum*2; #变量var的值等于传入的参数值乘2
-> if var>=10 then #当var的值大于10时,id值会加1,否则减1
-> update abc set id=id+1;
-> else
-> update abc set id=id-1;
-> end if;
-> end $$
delimiter ;
call proc03(8); #调用存储过程,并传入参数8
call proc03(3); #调用存储过程,并传入参数3
循环语句 while ···· end while
delimiter $$ #修改默认结束符为$$
create procedure proc04() #创建存储过程proc04
-> begin #过程体以关键字begin开始
-> declare var int(10); #定义变量var为int类型
-> set var=0; #var的起始值为0
-> while var<6 do #使用while循环,当var值小于6时满足条件,则向表中插入var的值
-> insert into abc values(var);
-> set var=var+1; #每次循环后var值自增1
-> end while; #结束while循环
-> end $$ #创建存储过程结束
delimiter ; #重新修改默认结束符为原始的;
call proc04; #调用存储过程proc04
declare命令也可以用于shell中,例如:
decalre -i var 声明变量var为整数型( 即增加整型属性)。
decalre +i var 删除变量var的整型属性。