目录
count() 、sum ()、AVG()、Min()、Max()的使用
Strcmp(‘str1’,‘str2’)——字符串比较,比较原理是str1与str2逐个比较字符。
Substring(str,postion,length)——很有用的这个
Format(num,i)——保留i位小数(很有用的哦),按四舍五入机制
LIMIT int strat,int rows函数——用于分页查询
MySql三层结构
首先清楚mysql是一个安装在我们电脑上面的数据库管理系统,他的第一层就是mysql管理系统程序(mysqld),第二层就是数据库(data),数据库里面不止有数据表还有其他的东西比如视图、函数、事件等,但那是高级部分的知识点,但主要还是数据表。第三层就是数据表。
我们所有的对数据库的操作其实都是通过端口3306把指令发送给DBMS,然后这个DBMS就会拿到你的指令去解析去做相应的操作。
数据库——表的本质仍然是一个文件。
SOL语句分类
创建数据库
//不指定字符集和校对规则,默认为(字符集:utf8 校对规则:utf8_general_ci [区分大小写])
1.CREATE DATABASE yzh_db01; 2.CREATE DATABASE yzh_db02 CHARACTER SET utf8;
3.CREATE DATABASE yzh_db03 CHARACTER SET utf8 COLLATE utf8_bin
注意一个细节:在我们创建表的时候如果你不对表设置字符集和校对规则那么他会默认采用数据库的字符集和校对规则。
第二句的使用场景是查看某个数据库的相关信息,比如该数据库的字符集和校对规则等。
备份与恢复数据库
备份数据库的概念就是以文件的形式去和保存数据库。恢复就是把备份好的文件转成数据库。
备份:
在DOS层使用 mysqldump -u root -p -B 数据库名1 数据库名2 >备份到目标文件
这里面可以写入多个数据库,-u是用户名,-p是密码,-B是数据库
恢复数据库:在mysql下面执行 source 需要恢复的目标文件
执行步骤:1.先进入mysql,在dos层执行 mysql -u root -p 2. source 需要恢复的目标文件
其实你这个把数据库转成以文件的形式存在,文件里面保存的全都是sql语句,你在恢复的时候还可以用另外一种方式就是在比如navicat里面新建一个查询编辑器然后把文件里面的sql语句全部复制过来执行,这样也可以恢复数据库
创建表
数据类型
tinyint——极小的。smallint——微小的。mediumint——中等的。
数值细节:
一定要注意这个数值类型,他是分为带符号和不带符号的,带符号和不带符号他的取值范围是不一样的。数值类型默认情况下都是带符号的,符号指的是正负号。
其他的数值类型也是这样的
注意:DECEIMAL(M,D)。这个M表示总位数(包括整数部分和小数部分),D表示小数的位数。
比如说DECEMAL(5,2);表示我的总位数为5,其中小数部分为2.这个DECIMAL();一般还用来处理较大的整数,比如当你的整数连bigint都存不下了,那么就可以用decimal来做,只需把让m为0然后设置D即可。
字符串细节:(重要)
CHAR(int num)存储空间范围在0-255个字符。
num表示的是最多能存储num个不区分中英文的字符。但是假如num为4,则char(4),表示最多能存入4个字符,只要这个num在0-255之间就可以。他在分配空间的时候直接给你这个字段char对应的属性分配4个字符空间。
VARCHA(int num) 存储空间范围在0-65535个字节,varchar它存储的空间是可变的。
num表示的是最多能存储num个不区分中英文的字符。但是假如num为100,则varchar(100),那么你可以存入100个不区分中英文的字符。但是他的存储空间取决于两个点:
1.实际写入的字符个数 2.表的字符编码。
先说说varchar的存储范围,首先一定不能认为他能存入0-65535个字符。其实他真正能存入的范围在65532个字节,因为需要预留1-3个字节来保留varchara字段长度的实际大小
在字符编码为utf8的表中num不能超过65532/3 = 21884个字符。(utf8一个字符占用3字节)。还有那个实际长度是这样的,假如我写入的字符个数为50,则占用空间为50*3 = 150字节+1到3个字节。
在字符编码为gbk的表中num不能超过65532/2 = 32776。(gbk一个字符占用2字节)。假如我写入的字符个数为50,则占用空间为50*2 = 100字节加上1到3个字节。
日期类
一些常用的指令:
mysql -u -p; ——进入mysql层
show databases; ——查询所有的数据库信息
create database db_name character set utf8 collate utf8_bin; ——创建数据库 注意后面的字符编码和校对规则是可以不写的,不写的话他默认就是utf8,校对规则默认是utf8_general_ci 这个默认的校对规则表示不区分大小写.
drop database db1;——删除数据库db1(慎用)
show create database data_name;——查询数据库data_name的相关创建信息。
注意此查询的是该数据库的字符编码(characte)等信息。
use data_name;切换(选择)数据库命令
select database();——查看当前数据库。这个查看并不是查看里面的东西,而是查看当前mysql命令定位在哪一个数据库。
alter database db1 character set gbk;——修改数据库的字符编码为gbk
alter database db1 collate utf8_general_ci;——修改数据库的校对规则为utf8_general_ci
show tables; 查询当前数据库的所有表
show create table tb_name; 查询表创建的相关信息(一般用来查询表的字符编码以及校对规则)
rename table 当前表名 to 目标表名;—— 修改表名
加上unsigned表示数值不带符号,默认数值字段是带符号的
练习:创建表
desc tb_name; 查询表的所有列
alter table tb_name modify 列名 相关属性; ——修改表中某列的相关属性、
alter table tb_name change `当前列名` `目标列名` varchar(60) not null default ' ';
——修改tb_name表中某列名
alter table tb_name drop t1;——删除表的t1列
表的增删改查(包含去重函数distinct)
表中的增删改查语句都不需要加table这个关键字,直接写表名即可因为他们四个增删改查的关键字本就是专用来操作这个表的关键字。
insert语句:
insert into tb_name(字段1,字段2...)
values(字段值1,字段值2...)(这个后面是可以放多个括号的表示增加多条记录);
细节1:into()括号里面对应values()的值,如果values的值是完整的表字段列,那么into()这个括号
可以不写。前提是必须能完整的对应否则会报错。
细节2:当某字段没有为其赋值他的首选默认值为你在创建表的时候为该字段设置的默认值defalue值,
如果没有为该字段设置defalue值则为null,若该字段设置了not null(不为空)则会报错。
细节3:如果你的字段属性为int,那么你在赋值的时候是可以增加 '',单引号的,因为他底层会帮逆
尝试着把字符串转成int类型,但是前提是你的单引号你里面必须为可转int的内容。比如‘100’
Update语句:
Update tb_name
set 字段名1 = 修改值,
字段名2 = 修改值
where 限制条件(比如id = 12)
限制条件2;
细节1:如果不加限制条件where则会把该表中的所有字段名2的值都修改。
细节2:对某个数值属性的字段修改
update employee
#set salary += 10000; 错误写法
set salary = salary + 10000;
delete语句:
delete from tb_name
where 限制条件1(比如id = 13)
限制条件2;
细节1:不加where则表示删除表中所有的记录(慎用);
细节2:不可以删除某一列的数据,没有这样的操作。如果你真的有这个需求顶多使用
update来修改是某一列的记录全部设为null。
细节3:update仅删除的是表的记录无法删除表,若要删除表需要用 drop table 表名; 操作
select语句:我用一个表来演示
我有这样一个表
基本使用:
select * from student; #查询所有的记录
select * from db_name.tb_name; -- 可以指定查询某个数据库下面的表
select `name`,`english` from student #查询所有的名字+英语成绩
select distinct `english` from student #查询所有的英语成绩并把重复的去掉
细节1:这个distinct的去重机制是,你所查询的所有字段必须一致才会去重,比如你查的是名字
和英语成绩,那么必须是英语成绩和名字相同才会去重。
进阶一丢丢:
#计算所有学生的总分,并且查询结果使用别名
select `name` as '名字', (chinese + english + math) as '总分' from student
细节1:如果你不是用别名的话比如总分他的字段名就会是(chinese + english + math),名字就是name
//注意点:between ... and ... 是一个闭区间 ;
这个IN(100,200)里面并不是查询100-200区间的值,而是查询100或200的值
我们来练习一下,还是刚刚那个表
查询math大于60 并且(and) id大于3的学生成绩
select * from student
where math > 60 and id > 3;
查询英语成绩大于语文成绩的同学
select * from student
where english > chinese;
查询总分大于200分 并且 数学成绩大于语文成绩,的姓韩的学生.
select * from student
where (chinese + math + english) > 200
and math > chinese
and `name` like '韩%'
细节1:最后一个百分号指的是不管你是韩顺平还是韩宇他都给你查出来。因为他是模糊查询
-- 1.查询英语分数在80-90之间的同学。
select * from student
#方式一 where `english` > 80 and `english` < 90
#方式二 where `english` between 80 and 90;
-- 2.查询数学分数为30,77,90的同学。
select * from student
#方式一 where `math` in(30,77,90)
#方式二 where `math` = 30
# or `math` = 77
# or `math` = 90
-- 3.查询所有姓张的学生成绩。
select * from student
where `name` like '张%'
-- 4.查询数学分>80,语文分>80的同学。
select * from student
where `math` > 80 and `chinese` > 80
-- 课堂练习[学员自己练习]
-- 1. 查询语文分数在 70-80之间的同学。
select * from student
where `chinese` between 70 and 80
-- 2.查询总分为170,233,257的同学。
select * from student
where (`chinese`+`math`+`english`)
in(170,233,257);
-- 3. 查询所有姓张 或者 姓欧 的学生成绩。
select * from student
-- where `name` like ('张%' or '欧%') #错误写法
where `name` like '张%' or `name` like '欧%'
-- 4.查询数学比语文多1分的同学。
select * from student
where (`math` - `chinese`) = 1
Order by排序函数的使用
Order by函数用来对查询的结果进行排序。他是放在最后面的,核心思想是,先使用select把你所需要排序用到的数据先查出来,最后在末尾使用Order by 进行排序。
Asc——Acending 升序
Desc——Descending 降序
不加排序规则的话,默认是Asc升序排序。
还是刚刚那张表
-- 对数学成绩排序后输出升序
select * from student
order by math asc
-- 对总分按从高到低的顺序输出
select * ,(chinese + english + math) as `total` from student
order by `total` Desc;
insert into student
values(8,'韩信',88,60,89);
-- 对姓韩的学生成绩排序输出(升序)
select * , (chinese + english + math) as total from student
where `name` like '韩%'
order by `total` asc
count() 、sum ()、AVG()、Min()、Max()的使用
coutn()的使用注意点:
count(*) 表示返回整个表的记录的行数(null也会返回)。
count(A字段) 表示返回A字段不为null的所有记录行数。
sum函数的使用注意点
一般是sum(数值字段名)的形式来使用,注意sum函数用来统计某数值字段的总合
他仅对数值取作用。
count和sum里面是可以放表达式的
avg()函数:他用来做平均数
min和max:顾名思义不再赘述
-- 统计一个班的总人数
select count(*) from student -- 他会返回所有记录的行数
select count(`name`) from student -- 他会返回所有name不为null 的行数
-- 统计一个班级数学总成绩?
select sum(math) from student
-- 统计一个班级语文、英语、数学各科的总成绩
select sum(chinese) as `chinese_total`,
sum(math) as `math_total`,
sum(english) as `english_total`from student
-- 统计一个班级语文、英语、数学的成绩总和
select sum(chinese + math + english) from student
-- 统计一个班级语文成绩平均分
select sum(chinese) / count(*) from student -- 方法一
select avg(chinese) from student -- 方法二
-- 求班级总分最高分和最低分
select Max(chinese + math + english),
Min(chinese + math +english)
from student;
group by + having ——分组函数
注意:
1、聚合函数:分组之后进行计算;
2、通常 select后面的内容是被分组的列,以及聚合函数;
分组有一个特点:
一旦使用了分组函数,那么最终在显示的时候,只能显示被分组的列或者聚合函数。3.分组必须把分组依据结合分组的语法考虑进去。
分组函数的思想:
group by 是分组依据的意思,having你可以把它理解为一个过滤条件(他是在分完组后在组中过滤的)。
基本使用:select 需要展示的字段1,字段2... from tb_name
group by 字段名3 + having 过滤条件;
核心思想是:从程序的角度来看是先把你查询出来的记录进行分组group by 再 过滤 having)在实际过程中可以先考虑分组再考虑打印样式)
注意点:这个分组标准可以是多个的,比如
我 要 查询每个部门不同的岗位的平均工资。
这个你怎么分组??首先明确你的分组标准,这里显然有两个——部门和岗位,然后在确定你要打印的样式。
-- 创建数据库
DROP DATABASE IF EXISTS mydb;
CREATE DATABASE mydb;
USE mydb;
-- 创建员工表
CREATE TABLE employee (
id int,
name varchar(50),
salary int,
departmentnumber int
);
-- 向员工表中插入数据
INSERT INTO employee values(1,'tome',2000,1001);
INSERT INTO employee values(2,'lucy',9000,1002);
INSERT INTO employee values(3,'joke',5000,1003);
INSERT INTO employee values(4,'wang',3000,1004);
INSERT INTO employee values(5,'chen',3000,1001);
INSERT INTO employee values(6,'yukt',7000,1002);
INSERT INTO employee values(7,'rett',6000,1003);
INSERT INTO employee values(8,'mujk',4000,1004);
INSERT INTO employee values(9,'poik',3000,1001);
我现在有这样一个表:
以下查询需求可以先考虑分组和过滤的条件,在来考虑查询的样式
-- 统计各部门的员工个数:
select departmentnumber,count(*) from employee
group by departmentnumber;
解读:代码分析,如果你不加分组条件 group by departmentnumber;
你的查询结果是 “不同的部门 公司总人数”
这样不仅不满足我们的查询条件且他会报错,告诉你必须加分组否则对应不上。
所以我们其实是先查出 “不同的部门 公司总人数 ” 这样的一条条记录
然后在对他们分组。
其实可以先考虑分组条件,再考虑查询的样式。这样会更容易理解
-- 统计部门编号大于1001的各部门员工个数
方法一:
select departmentnumber, count(*) from employee
group by departmentnumber
having departmentnumber > 1001;
解读:先考虑分组——部门分组。然后把部门编号小于1001的过滤掉。
输出样式就是“部门 人数个数”
方法二:
select departmentnumber,count(*) from employee
where departmentnumber > 1001
group by departmentnumber;
查询结果都是这样
-
-- 统计工资总和大于8000的部门,并且按总工资排序升序查询 select departmentnumber,sum(salary) as total_salary from employee group by departmentnumber having total_salary < 8000 order by total_salary asc; 解读:我的分组条件时部门,把工资总和低于8000的部门过滤掉,最后在升序排序。 然后我的查询样式是 “部门 工资总和” 这里面还有一个细节就是我用了别名,不用别名也可以只不过不用的话 你后面在过滤的时候还要计算一遍工资的总合—— having sum(salary) < 8000; 这样的话效率就会低一些
字符串函数:
charset——返回字符编码
select charset(字段名) from 表名;——返回该表该字段,所有记录的字符编码(字符集)
concat——拼接字符串
select concat(字段1,‘字符串’,字段2)from 表名;里面可以写多个字段和自创字符串
Inster(string,substring)
—— 返回substring在string中出现的位置,索引从1开始,没有返回0
select inster ('abcd','bc') from DUAL; 返回2
这个DUAL这一个系统自带的虚拟表,当你没有表可用的时候可以用它来做测试用。
UCASE(string) 将字符串转成大写
select UCASE(ename)from 表名
LCASE(string) 将字符串转成小写
select LCASE(ename)from 表名
Replace——替换
select ename,Replace(job,'MANAGER','经理')from emp;
在emp表中在字段为job这一列,把记录为MANAGER换成经理
Strcmp(‘str1’,‘str2’)——字符串比较,比较原理是str1与str2逐个比较字符。
(得清楚逐个比较字符:按下表索引逐个对比当某个字符不一样时,大于返回1,小于返回-1,等于返回0)
select Strcmp(‘abc’,‘abb’)from DUAL;返回1
select Strcmp(‘abc’,‘acb’)from DUAL;返回-1
select Strcmp(‘abc’,‘abc’)from DUAL;返回0
Substring(str,postion,length)——很有用的这个
——从字符串str中,第potion个位置开始,取length个字符,注意postion必须从1开始
select substring(ename,1,2) from emp;
——从emp表中,在ename字段中的所有记录都从第1个开始取两个字符。
练习:查询emp表中以首字母为小写的所有名字
关于数学的一些函数
ABS——取绝对值函数
select ABS(-1) from DUAL; ——结果为1
BIN——十进制转二进制
select BIN(10) from DUAL; ——结果为1010
Celling(num)——向上取整
注意:向上取整是取比num大的最小整数的意思
select Celling(-1.1) from DUAL; ——结果为-1
select Celling(1.1) from DUAL; ——结果为2
Floor(num)——向下取整
注意:向下取整是取比num小的最大整数的意思
select Floor(-1.1) from DUAL; ——结果为-2
select Floor(1.1) from DUAL; ——结果为1
CONV(num,当前进制,目标进制)——进制转换函数
select conv(8,10,2) from DUAL;把十进制的8转换成2进制
结果为1000
Format(num,i)——保留i位小数(很有用的哦),按四舍五入机制
select format(75.12165,2) from DUAL; ——75.12
Rand(seed);——随机数[0,1)
好比如java中学过的random啊,特殊随机百分比。他也是取不到1的啊。
里面这个seed是一个int型,他是一个种子的意思,就是说比如
select rand(3)from DUAL;
种子为3,每次返回的随机数rand(3)都是和第一次返回的随机数一样的。
当然你要是换了一个种子就不一样了
时间函数
SELECT CURRENT_DATE() from DUAL;//查询当前日期(年-月-日)
SELECT CURRENT_TIME() from DUAL;//查询当前时间(时:分:秒)
SELECT CURRENT_TIMESTAMP();//查询当前时间戳(年-月-日 时:分:秒)
SELECT NOW();//查询当前 年-月-日 时:分:秒
//给定一个时间戳(年-月-日 时:分:秒)查询他的日
SELECT DAY (NOW());
//给定一个时间戳(年-月-日 时:分:秒)查询他的年-月-日
SELECT DATE (NOW());
//给定一个时间戳(年-月-日 时:分:秒)查询他的时:分:秒
SELECT TIME (NOW());
//给定一个时间戳(年-月-日 时:分:秒)查询他的年
SELECT YEAR (NOW());
//给定一个时间戳(年-月-日 时:分:秒)查询他的月
SELECT MONTH (NOW());
//查询两个时分秒相加,返回结果还是时分秒
SELECT ADDTIME('14:23:12','01:02:01');
//在当前时间的基础上加上一天。这个Interval(因特儿窝)是间隔的意思
SELECT DATE_ADD(NOW(),Interval 1 DAY);
//在当前时间的基础上加上一个月。这个Interval(阴特窝)是间隔的意思
SELECT DATE_ADD(NOW(),INTERVAL 1 MONTH);
//在当前时间的基础上减去一天。这个Interval(阴特窝)是间隔的意思
SELECT DATE_SUB(NOW(),INTERVAL 1 DAY);
//在当前时间的基础上减去一个月。这个Interval(阴特窝)是间隔的意思
SELECT DATE_SUB(NOW(),INTERVAL 1 MONTH);
//查询两个日期间隔多少天
SELECT DATEDIFF('2019-07-22','2019-05-05');
注意他是用第一个日期减去第二个日期。他里面不只是可以用date类型,也可以是datetime或者timestamp
比如SELECT DATEDIFF('2019-07-22 12:36:01','2019-05-05 15:16:03');
注意点:stamp——邮戳
current_timestamp——时间戳 和now都是返回年月日时分秒但是时间戳current_timestamp他放入数据库会随着时间改变。now只会返回执行语句的时间,他在数据库中不会随着时间自动改变。
date_sub和date_add里面的interval后面的单位可以是年月日时分秒(year、month、day、hour、minute、second)
案例:
假如我有这样一个表
-- 显示十分钟以内发布的所有新闻(这个一定要会)
select * from mes
where data_add(send_time, interval 10 minute) >= 10 now();
//解读:把你发送的时间加上十分钟如果大于当前时间说明你这个发送的时间距离当前时间小于十分钟,实在不懂画一个时间线你就懂了
那你会了这个以后你就得举一反三,
比如:显示一年以内发布的所有新闻
select * from mes where data_add(send_time , interval 1 year) >= now();
千万别给我写成加号了,要用关键字interval(因特儿窝)
当然也可以用data_sub来做,还是刚刚那道题显示十分钟以内发布的所以新闻
select * from mes
where data_sub(now() , interval 10 minute) <= send_time;
-- 你活了多少天了?
SELECT DATEDIFF(DATE(NOW()),'2000-01-02') FROM DUAL;
-- 查询你的年龄
SELECT DATEDIFF(DATE(NOW()),'2000-01-02') /365 FROM DUAL;
-- 如果你能活到80岁,求出你还能活多少天
SELECT 80*365 - DATEDIFF(DATE(NOW()),'2000-01-02');
unix_timestamp()和from_unixtime
select unix_timestamp();——返回1970-1-1到现在的秒数
select from_unixtime(int minute, '%Y-%M-%D') ;——把秒数转成date
select from_unixtime(int minute, '%Y-%M-%D %H:%i:%s') ;——把秒数转成datetime
from_unixtime这个在开发的过程中有很大的实用价值,可以用一个数值当代表一个时间,那么他底层就是用from_unixtime来转换的
加密函数
加密函数有两种:MD5() 和password()
password用于修改mysql的用户密码,如果是应用与web程序建议使用md5()函数,
password函数旧版16位,新版41位,可用select length(password('123456'))察看。
password函数加密不可逆,如果和数据库里加密后内容比较时可以采用password(pwd)==字段内容的方式;
md5函数加密后32位,此加密算法不可逆,其实md5算法是信息摘要算法,如果拿来做压缩也是有损压缩,理论上即使有反向算法也无法恢复信息原样。他常被用来检验下载数据的完整性。如好多软件都提供md5码,供用户下载完毕校验完整性。
//看看就行,主要知道他们的使用方法
-- 创建一个表来演示MD5
CREATE TABLE pwd_test
(id INT(10),pwd CHAR(32));-- 这里面当你知道你要用md5加密来作为密码那你就可以直接把使用char(32)
INSERT INTO pwd_test
VALUES(1001,MD5('yang0932.'));
-- 使用md5加密过后不管你的密码长度是多长他都会转换成32位的字符,这也是前面在创建表的时候为啥要把密码设为char(32)
存入数据库的密码是这样的
-- 演示password 还是前面那个表。
ALTER TABLE pwd_test MODIFY pwd CHAR(100);
-- 注意如果你要使用password加密的话它的长度可就不止32位了所以我这里先改变一下字段的属性
SELECT * FROM pwd_test;
INSERT INTO pwd_test
VALUES(1002,PASSWORD('yang0932.'));
流程控制语句
IF(expr,num1,num2)
-- 如果expr为真则返回num1,否则返回num2,
-- 这个类似三元运算符
SELECT IF(3>2,100,666); -- 返回100
-- 细节:如果你要用if判断是否为空,必须用 is 不能用 = ,判断不为空使用 is not
比如 if(num is null,num1,num2); if(num is not null,num1,num2);
IFNULL(num1,num2); -- 如果num1不为null则返回num1,否则返回num2
SELECT IFNULL(NULL,100); -- 返回100
CASE -- case:案例
WHEN expr1 THEN num1 -- when:在什么时候 Then:然后
WHEN expr2 THEN num2
... -- 可以加多个when Then
ELSE num3 END
-- 如果expr1为真则返回num1,否则执行expr2,若expr2为真则返回num2。否则返回num3
-- 这个就是类似于if else if ... else 多重分支
查询加强
where函数
-- ?如何查找1992.1.1后入职的员工如何使用like操作符(日期比较——前提是两个日期的格式必须一致)
SELECT * FROM emp
WHERE hiredate > '1992-01-01'
-- %:表示0到多个字符 _:表示单个字符(单个下划线表示一个字符)
-- 如何显示首字符为S的员工姓名和工资
SELECT ename,sal FROM emp
WHERE ename LIKE 'S%'
-- 如何显示第三个字符为大写O的所有员工的姓名和工资
SELECT ename,sal FROM emp
WHERE ename LIKE '__O%' -- '__O%'这里面有两个下划线
-- 如何显示没有上级的雇员
SELECT * FROM emp
WHERE mgr IS NULL
-- 查询表结构selectinc.sql
DESC emp
order by
解读:先按照部门号升序排序,在按照员工工资降序排序,也就是说在前面部门号排序的基础上再把员工工资降序排序
分页查询(很重要,开发一定会用到)
思考:如果我有一个员工表,他里面有10万个员工数据记录,按照我们以前查询表的方式,那你不可能一次性把10万个员工的信息一次性查询出来放到一张表里面吧??这显然不现实,所以我们使用分页查询,分页查询的概念就是把一个表分割成多个页。
LIMIT int strat,int rows函数——用于分页查询
下面这个公式记一下,看下面的案例你就懂了
多表查询(重要)
首先我们在实际开发中肯定会涉及到多表查询的业务的。顾名思义多表查询就是需要通过查询多个表来达到业务需求。
select * from tb1,tb2;
当你这样查询两个表的时候会把tb1中的每一条记录与tb2中的每一条记录拼接组合成一条新的记录。比如tb1中有2条记录,tb2中有3条记录,则最后查询的结果会有 2*3=6 条记录。
这样的默认处理返回的结果称为笛卡尔集
老韩的解读:
引出点:因为笛卡尔集是通过全覆盖组合的方式组合而成的记录,那么里面肯定有许多无效记录,就是信息对应不上的,那么我们其实在进行多表查询的时候最主要的就是过滤笛卡尔集得到有效的记录。
老韩技巧:你在写过滤条件where的时候你的过滤条件必须不能少于 表的个数 - 1 否则肯定是不真确的他还是会出现笛卡尔集
下面是一些练习实践:
我现在有这样三张表,分别是员工表,部门表,工资等级表
-- ?显示雇员名,雇员工资及所在部门的名字
select ename,sal,dname from emp,dept
where emp.deptno = dept.deptno
-- ?如何显示部门号为10的部门名、员工名和工资
select dname,ename,sal from dept,emp
where dept.deptno = emp.deptno and dept.deptno = 10
-- ?显示各个员工的姓名,工资,及其工资的级别
select ename,sal,grade from emp,salgrade
-- 法一:where sal >= losal and sal <= hisal
-- 法二:where sal between losal and hisal
-- 学员练习:显示雇员名,雇员工资及所在部门的名字,并按部门排序[降序排].
select ename,sal,dname from emp,dept
where emp.deptno = dept.deptno
order by dname desc
技巧:首先分析所需要的查的字段对应的表,然后就是分析where了,如果你无法明确的分析出过滤的条件,那么你就查出两个表的笛卡尔集然后去分析。
自连接(重要)
思考:我现在要求你显示员工的名字和他上级的名字你要怎么查
我们发现员工的名字和他上级的名字都是在同一张表中——emp,每一条记录都代表一个员工,员工之间的关系有上下级关系,mgr字段代表该员工的上级工号——是empno字段。
那么我们的做法还是考虑多表查询
select ename,ename from emp,emp——错误,你不能这样写,得给他们分别去别名
select worker.ename as '员工名',manager.ename as '上级名'
from emp as worker,emp manager
where worker.mgr = manager.empno;
突破口就在于每一条记录的mgr都是他上级的工号
表取别名可以不需要用as(写了也不会报错),这个as的使用一般情况下是字段取别名
子查询(重要)
-- 显示与smith同一个部门的所有员工的信息
按照以前的方法:你可能会考虑用多表查询中的自连接——确实可以做但是复杂!!!
select other_emp.ename,other_emp.deptnofrom emp smith_emp,emp other_emp
where smith_emp.ename = 'smith' and -- 首先我的史密斯表我只要史密斯的信息
smith_emp.deptno = other_emp.deptno and -- 其次我把史密斯的部门号去对应另外一张表的部门编号smith_emp.ename != other_emp.ename -- 如果你只想查询smith同部门员工的信息你还得把另外一张表的史密斯信息过滤掉
-- 显示与smith同一个部门的所有员工的信息
分析:突破点就在于smith的部门编号和他部门其他人的部门编号是一样的
那我可以先查出史密斯的部门编号,然后在按照史密斯的部门编号去查即可
子查询方法:
smith部门编号为:select deptno from emp
where ename = 'smith';
则 smith部门的员工信息为
select * from emp
where deptno = (
select deptno from emp
where ename = 'smith'
)
//如果你只想查询smith同部门员工的信息还可以加 and ename != 'smith'
这就是子查询,这个是单行子查询,因为你的这个“子”只返回一行数据
多行子查询:
学了前面的单行子查询,那么多行子查询就是这个"子"有多个返回值我们把这多个返回值当做过滤的条件去查询
-- 我现在有一个需求,比如我在10号部门,我需要查询别的部门和我的部门相同岗位
-- 的员工信息——名字、岗位、工资、部门号,不包含我的部门的员工信息
-- 思路:我需要知道我的部门有哪些工作岗位——他的返回值就是一个或者多个工作岗位,
如果我这个"子"只有单个数据则直接用子查询,但是如果有多个我们就用in(子返回的数据)
关键字“遍历”着多个数据。这个遍历不一定恰当但是也就那个意思吧只要在他里面的就得过滤。
如果你不知道这个子能返回对少个数据也可以用这个in,因为就算你里面只有一个也是可以的。
select job from emp
where deptno = 10
select ename,job,sal,deptno from emp
where job in(
select job from emp
where deptno = 10)
and deptno <> 10
-- 查询每个部门工资最高的员工
select ename,deptno,sal from emp
where sal in(select max(sal) from emp
group by deptno)
all和any的使用
-- 显示工资比30号部门所有员工工资高的员工姓名、工资、部门号
select ename,sal,deptno from emp
where sal > all(select sal from emp
where deptno = 30);select ename,sal,deptno from emp
where sal > (select max(sal) from emp
where deptno = 30);-- 显示工资比30号部门任意员工工资高的员工姓名、工资、部门号
select ename,sal,deptno from emp
where sal > any(select sal from emp
where deptno = 30);select ename,sal,deptno from emp
where sal > (select min(sal) from emp
where deptno = 30);
多列子查询
-- 查询与allen部门和工作岗位完全相同的人,不包括allen本人
select *
from emp
where (deptno,job) = (
select deptno,job
from emp
where ename = 'allen')
and ename != 'allen'
-- 查询与宋江语文数学英语成绩完全相同的人
select *
from student
where (math,chinese,english) = (
select math,chinese,english
from student
where `name` = '宋江')
练习
-- 查找每个部门工资高于本部门平均工资的人
-- 先求出每个部门的部门号和平均工资
select deptno,avg(sal) as avg_sal
from emp
group by deptno
-- 把这个表当做一个临时表与emp表一起用作多表查询
select ename,emp.deptno,job,sal,avg_sal
from emp,(
select deptno,avg(sal) as avg_sal
from emp
group by deptno
) as emp2
where emp.deptno = emp2.deptno and emp.sal > avg_sal
-- 查找每个部门工资最高的人的详细信息
select *
from emp
where sal in(select max(sal) from emp
group by deptno)select emp.*
from emp,(select max(sal) as max_sal
from emp
group by deptno) as maxsal_table
where emp.sal = max_sal
-- 查询每个部门的名字、编号、地址、以及部门的人数
-- 首先 部门名字、编号、地址——dept表。 部门人数——emp表
select dname,dept.deptno,loc,headcount
from dept,(select deptno,count(*) as headcount
from emp
group by deptno) as emp2
where dept.deptno = emp2.deptnoselect dept.*,headcount
from dept,(select deptno,count(*) as headcount
from emp
group by deptno) as emp2
where dept.deptno = emp2.deptno
复制表 与 去重
create table copy_emp like emp
insert into copy_emp (select * from emp) —— 这里不需要再使用valuse了-- 这种复制方式效率很高,他会一次性插入多行数据,应用场景是在测试sql语句的时候会用这种方式创建海量的数据用于测试。
复制表的关键在于插入表与被复制表的每一个字段需要匹配否则会失败。
去除表中的重复记录:emp为例(我现在emp表中包含重复的记录)
思路:
- 创建一张新的表copy_emp,表的结构和emp表一样
- 查询emp表中去除重复后的记录,使用关键字distinct
- 把所查询的记录插入到copy_emp。
- 可以删除emp然后把copy_emp的名字改为emp。或者你也可以先把emp表的数据清空然后再把copy_emp的数据复制过来。如下代码演示
- create table copy_emp like emp
insert into copy_emp (select distinct * from emp)
drop table emp
alter table copy_emp rename emp
和并查询
-- union all 合并返回查询的结果,不会去掉重复的记录,
select * from emp where ename = 'smith'
union all
select * from emp where ename = 'smith'-- union 合并返回的查询结果,会去掉重复的记录
select * from emp where ename = 'smith'
union
select * from emp where ename = 'smith'
左外连接和右外连接
我现在有这样两张表:学生表(stu)与成绩表(score)
stu score
需求:查出所有的学生id、姓名、成绩,如果没有成绩则显示成绩为null
按照以往的多表查询方法
select stu.id,`name`,`score`
from stu,score
where stu.id = score.id
很明显这个查询结果不满足条件,id为3和4的吉米和格林哪去了???(引出左外连接)
左外连接:取出左表中所有符合需求的记录,若无法通过过滤条件与右表的记录相匹配则返回右边的结果为null。
使用方式:from 左表 Left Join 右表 on 过滤条件 (在这里on作用就是过滤条件的关键字与where作用相等)
select stu.id,`name`,`score`
from stu left join score
on stu.id = score.id运行结果:
把stu当做左表。score当做右表
需求:查出所有的学生id、姓名、成绩,如果没有名字则显示名字为null
很明显按照以往的方式多表查询肯定是达不到需求的,这个时候我们使用右外连接
右外连接:取出右表中所有符合需求的记录,若无法通过过滤条件与左表记录相匹配接则返回左表的结果为null。
使用方式:from 左表 Right Join 右表 on 过滤条件 (在这里on作用就是过滤条件的关键字与where作用相等)
select stu.id,`name`,`score`
from stu Right join score
on stu.id = score.id运行结果:
左右外连接的使用其实并复杂,关键是根据需求找出必须要查询的字段,比如上面展示的左外连它的需求就是必须查询所有的名字,然后上面展示的右外连接他的需求则必须查询所有的成绩。
主键的细节
复合主键的概念是两个字段加在一起依据,比如(name+id)为复合主键,在你添加记录的时候只有两个主键的值和前面的记录都相同他才会报错,如果其中一个相同另外一个不相同则是允许添加成功的。
复合组件只能在表定义完后写,如果你直接在两个字段名后面加primakry key他会报错
复合主键的视图结构是这样的
你可千万别乱跟别人讲id是第一个主键,name是第二个主键。大错特错,在主键这个层面他两是相关连的。
还有一个就是在sql中定义主键的两种格式写法:
第二种方式是在括号里面的,复合主键必须用第二种方式
unique——约束重复
使用环境:当你不想让一个非主键字段重复的时候可以用unique,或者需求字段可以为null但不能重复的时候就可以用这个unique,他在视图结构是下面这样的
外键约束
第三条插入失败的原因是,你的class_id必须要是在主表中id有的才行。 并且,因为我现在tom的class_id是外键且已经与班级表中的id相匹配了,你要是想直接删除班级表中的第一条记录他会失败,除非你在学生表中把tom这条记录先删除才可以去删除班级表中的第一条记录 。
foreign key (外键字段) references 主表名(匹配字段)
check约束(了解即可)
约束建表练习:
自增长(重要)
auto_increment auto:自动 increment:增长
自增长一般就是序列号字段,他不可重复,也就是说要么配合搭配primary key 要么搭配unique来使用。一般来说如果你指定了这个字段为自增长,那么就非常不建议再添加记录的时候还给他赋值,因为这样很矛盾,一般直接赋值为null,然后如果是赋值null他就会默认在上一条记录的基础上增长1。直接演示吧
CREATE TABLE increment(
num_order INT PRIMARY KEY AUTO_INCREMENT)INSERT INTO increment VALUES(NULL)
SELECT * FROM increment在执行一次:
INSERT INTO increment VALUES(NULL);
SELECT * FROM incrementINSERT INTO increment VALUES(5);
SELECT * FROM incrementINSERT INTO increment VALUES(NULL);
SELECT * FROM incrementDELETE FROM increment WHERE num_order = 6
INSERT INTO increment VALUES(NULL);
SELECT * FROM increment-- 这个自增长字段值是唯一的,就算你把它给删了你在插入他也不会重复你前面删掉的那个记录中的自增长字段,除非你指定赋值你删掉的个记录中的自增长字段
INSERT INTO increment VALUES(6);
SELECT * FROM increment
如果一开始没有对主键或unique字段设置自增长,你在后面的可以通过下面的语句进行修改
索引(重要)
索引是在我们的数据记录中形成一个二叉树,他的速度是非常非常快的,比如你查询30次,假如没有添加索引那么他的效率就是30,因为他是全表扫描,即使你在第一条记录中查到了他也会往后面继续查询,因为他不知道你后面还有没有一样的,所以是全表扫描完后才会返回结果,但是如果是二叉树,你查询30次,他的覆盖面积是2的30次方,它可以覆盖10亿多的数据,效率是非常之恐怖的。
primary key这个你创建了以后他就自带索引了无需多言,index的应用场景是比如你是允许出现重复的值的时候同时有需要提升查询效率的就可以用这个。
-- 查询表的索引
-- 添加索引(两种方式)
-- create 索引类型(unique index、index) 索引名 on 表(字段)
CREATE UNIQUE INDEX id_index ON test_index(id)
-- alter table 表名 add 索引类型(unique index、index) (字段)
ALTER TABLE test_index ADD UNIQUE INDEX id_index (id)-- 注意如果你的test_index表中id为主键则不需要再为它添加索引了,因为主键就是索引。或者你后面再添加主键
ALTER TABLE test_index ADD PRIMARY KEY (id); -- 这样也可以
-- 删除索引
DROP INDEX id_index ON test_index
-- 删除主键索引
ALTER TABLE test_index DROP PRIMARY KEY注意:如果先为id添加了普通索引或者unique索引,然后你又为id添加主键索引他是可以添加成功的,并且当你要分别对应你添加的主键或者索引删除两次才能把他这个字段的索引删除干净。
修改索引就是需要你把已有的索引删除再添加新的索引。
事务机制
-- 假如我现在有这样一张表
CREATE TABLE balance(
`name` VARCHAR(30),
money DECIMAL(10,2))
-- 插入两条记录
INSERT INTO balance(`name`,money)
VALUES('tom',3000);
INSERT INTO balance(`name`,money)
VALUES('jack',6000);
-- 我现在有一个这样的需求:
-- tom向jack转账100元。
UPDATE balance SET
money = money - 100
WHERE `name` = 'tom'
UPDATE balance SET
money = money + 100
WHERE `name` = 'jack'
-- 问假如我在执行完tom减掉100的操作后在执行第二句jack加100的
-- 操作时出现了错误,那么造成的后果就是tom的钱少了100,jack
-- 没有收到tom的转账100
--有上述代码引出事务机制
事务的操作机制是面向“增删改”的操作的
使用事务机制对刚刚那个案例进行修改演示:
CREATE TABLE balance(
`name` VARCHAR(30),
money DECIMAL(10,2));-- 插入两条记录
INSERT INTO balance(`name`,money)
VALUES('tom',3000);
INSERT INTO balance(`name`,money)
VALUES('jack',6000);
SELECT * FROM balance;
-- 我现在有一个这样的需求:
-- tom向jack转账100元。
START TRANSACTION; -- 启动一个事务SAVEPOINT a; -- 设置保存点a
UPDATE balance SET
money = money - 100
WHERE `name` = 'tom';
SAVEPOINT b; -- 设置保存点bUPDATE balance SET
money = money + 100
WHERE `name` = 'jack';
-- 问假如我在执行完tom减掉100的操作后在执行第二句jack加100的
-- 操作时出现了错误,那么造成的后果就是tom的钱少了100,jack
-- 没有收到tom的转账100。-- 那么因为现在我知道我在对jack加钱的这个操作是有错误的那么我就是用回滚
-- rollback到b保存点
ROLLBACK TO b; -- 执行完后,就相当于回退到b点
SELECT * FROM balance;b点位置此时数据表是这样的
然后我又发现在对tom操作减去100的操作也有错误那么我还可以用
rollback to a或者rollback回到a点或者事务开始的地方。
简单展示:
start transaction;/set autocommit = off; --两种开启事务方式
savepoint a; --设置a保存点
----------sql语句
-----------sql语句
savepoint b; -- 设置b保存点
----------sql语句
-----------sql语句
savepoint c; -- 设置c保存点
rollback to b; -- 回退到b保存点
rollback to a --回退到a保存点(如果直接回退到a这里,那么b保存点将会被删除,那么你回到a以后你就不能再从a回到b了,因为那个保存点b被你删掉了)
rollback ;-- 回退全部事务,直接回滚到事务开始的地位置并且他会删除所有的保存点
那么rollback to a和rollback;的区别就是前者回到a删除b,后者回到start transaction删除ab
commit; -- 表示提交事务,也就是说你这个事务中如果你认为没有任何问题了你就可以使用这个,commit提交事务,一旦提交事务,他会先把你的所有保存点删除,然后事务中的所有数据将会生效。
其实在你开启startaction事务的时候 他会给你分配一个默认的保存点,这也是为什么使用rollback;可以直接回滚到事务开始的地方。
事务机制的操作只能在存储引擎为innodb才支持。这也是innodb存储引擎最大的亮点。
mysql隔离界别
脏读、幻读和不可重复读
1、脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
(比如两个事务ab在同一时间段对同一个表访问操作,a在操作表的时候对表中的记录有修改,但是并未提交事务,此时b通过访问表竟然可以看到a修改的记录数据,但是b并未提交啊,此时若a出现错误回滚了事务那么b读到的数据就是脏读)
例如:
张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
与此同时,
事务B正在读取张三的工资,读取到张三的工资为8000。
随后,
事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
最后,
事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
2、不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
与此同时,
事务B把张三的工资改为8000,并提交了事务。
随后,
在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
3、幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
此时,
事务B插入一条工资也为5000的记录。
这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
具体使用需要另外参考完档,这种隔离级别的使用其实类似于api,随用随查。
存储引擎
//在最后执行查询t82你会发现无法回滚,。
视图
//这个当视图产生以后他并不会存放数据,他的数据来源于基表,你在操作视图的数据的时候其实他会通过映射的关系也改变基表的数据,同样的你修改了基表的数据也会影响到对应视图查询的数据。
//可以通过视图创建另外一个视图,另外一个视图的数据来源也是基表的数据。
-- 需求我需要查询员工的名字、部门名字、工资、工资等级
-- 那么我此时需要用到三张表,使用多表查询的方式可以完成
SELECT emp.ename,dname,sal,grade
FROM emp,salgrade,dept
WHERE emp.deptno = dept.deptno AND
(sal BETWEEN losal AND hisal)
-- 问题引出:我需要修改你上面查询到多表查询的数据,
-- 你得需要调用不同的表去修改数据才能达到修改的需求,
-- 且不说修改,你用多表查询你只能查一次,你下一次查你还得写这么多sql
-- 那我要是做了这样一个视图,我直接select * 视图名不是更方便??
-- 如果我把上述查询的结果做成一个视图我就可以仅对这个视图的数据
-- 修改,我修改这个视图的数据,他同样会映射到对应的基表修改
-- 这就是视图的灵活性
CREATE VIEW emps_view
AS SELECT emp.ename,dname,sal,grade
FROM emp,salgrade,dept
WHERE emp.deptno = dept.deptno AND
(sal BETWEEN losal AND hisal)
用户管理
应用场景:
select * from mysql.user;——查询mysql数据库用户
mysql用户他其实是放在mysql数据库中的user表中的。每一条记录都有很多关于权限的字段
创建用户
CREATE USER 'test_user'@'localhost' IDENTIFIED BY '123'
-- test_user是用户名,localhost是允许登录的地址,msql认为这两个连在一起才算一个完整的名字。
-- 注意在创建用户的时候可能会报错说:
-- MySQL服务器正在使用--skip-grant-tables选项运行,因此无法执行此语句
-- 我们只需执行下面的语句
FLUSH PRIVILEGES
删除用户
drop user 'test_user'@'localhost';
用户授权:
你自己后面做项目真的用到你再去学吧,暂时不赘述
mysql作业
//别名可以不加as,那么123都是正确的,最后一个你的别名出现空格,并且又不加单引号或者双引号mysql系统会错认为前面那一部分为别名,空格后面部分报错。
//主要是判断非空,sql语句中判断非空只能用 is not。判断空用is null
-- 2.写出 查看DEPT表和EMP表的结构 的sql语句
DESC dept
DESC emp
-- 3. 使用简单查询语句完成:
-- (1)显示所有部门名称。
SELECT dname FROM dept
-- (2) 显示所有雇员名及其全年收入 13月(工资+补助),并指定列别名"年收入'
SELECT ename,IF(comm IS NULL,13*sal,13*(sal+comm)) AS "年收入" FROM emp
-- 4.限制查询数据。
-- (1)显示工资超过2850的雇员姓名和工资。
SELECT ename,sal
FROM emp
WHERE sal > 2850
-- (2) 显示工资不在1500到2850之间的所有雇员名及工资。
SELECT ename,sal
FROM emp
WHERE !(sal BETWEEN 1500 AND 2850)
-- (3) 显示编号为7566的雇员姓名及所在部门编号。
SELECT ename,deptno
FROM emp
WHERE empno = 7566
-- (4)显示部门10和30中工资超过1500的雇员名及工资。
SELECT ename,sal
FROM EMP
where deptno in(10,30) and sal > 1500
-- (5) 显示无管理者的雇员名及岗位。
select ename,job
from emp
where mgr is null
-- 5.排序数据。
-- (1) 显示在1991年2月1日到1991年5月1日之间雇用的雇员名,
-- 岗位及雇佣日期,并以雇佣日期进行排序。
select ename,job,hiredate
from emp
where hiredate between '1991-2-1' and '1991-5-1'
order by hiredate desc
select ename,job,hiredate
from emp
where hiredate >= '1991-2-1' and hiredate <= '1991-5-1'
order by hiredate asc
-- (2) 显示获得补助的所有雇员名,工资及补助,并以工资降序排序
select ename,sal,comm
from emp
where comm is not null
order by sal desc
一些注意事项:
- where语句中只能使用字段名作为过滤依据,不能使用聚合函数count、sum和别名。
- group by的使用一定要把分组情况考虑到位完整。比如这道题,如果你不使用where的话,你必须考虑到他要你查询的是在选择07003这个课程号下面不同学号的成绩,那么首先你得把 课程号、学号 设为分组二者缺一不可。
- 在使用group by的时候首先得把where考虑进去,当你的where不足以达到你的过滤需求的时候你才使用group by。
- 去重机函数记一下
- 一定要严格按照这个顺序来否则他的语法层面可能就不会通过了
- count、avg、sum、min、max都是聚合函数, 聚合函数对一组值执行计算并返回单一的值。除 COUNT 以外,聚合函数忽略空值,如果COUNT函数的应用对象是一个确定列名,并且该列存在空值,此时COUNT仍会忽略空值。聚合函数经常与 SELECT 语句的 GROUP BY 子句的HAVING一同使用。不能用在where,因为where是逐条查询。但是如果你要在where中使用聚合函数,你可以写一条select子句去查询聚合函数,然后返回的值给where使用。
- insert into后面不一定要用values,比如你在复制表的时候是一次性插入多行记录的,values是一次性插入一条记录
- like关键字除了用于模糊查询还可以用来复制表的结构,比如:create table copy_tb like tb;
- 在设置double字段的时候double是有两个参数的double(数值长度,小数点个数);数值长度指的是包含小数和整数的位数。
- group by 点击跳转文章