0、数据库基础介绍
MySQL常用数据类型
数据库管理系统 DBMS(data base management system ),包括Oracle,mysql,SQLserver,MongoDB,db2等
数据库/仓库 DB
SQL 结构化查询语言
Mysql 一种数据库软件,使用sql语言管理数据
数据库中事务的四大特性(ACID):原子性(Atomicity);一致性(Consistency);隔离性(Isolation);⑷ 持久性(Durability)
SQL语句分类:
1、DQL:数据查询语句,select (Data Query Language)
2、DML:数据操作语句,insert/delete/update (Data Manipulation Language)
3、DDL:数据库定义语句,create/drop/alter (Data Denifition Language)
4、TCL:事务控制语言,commit/rollback (Trasactional Control Language)
5、DCL: 数据控制语言,grant ,revoke (Data Control Language)
创建数据库:
create database bjpowernode;
导入数据库脚本
数据库脚本:以.sql后缀结尾的文件
导入数据库脚本:
1. 选定数据库
2. source命令
mysql> use bjpowernode;
Database changed
mysql> source C:UserscopywangDesktopjpowernode.sql
数据库表的介绍:
mysql>show tables;
mysql>desc dept;
1、查询字段
select ename,sal,job from emp;
select * from emp; 不建议使用,效率比前一个方法要低,因为要把*转成所有具体字段
2、进行数学运算,并对字段重命名
select ename,sal*12 as yearsal from emp;
3、条件查询
运算符 |
说明 |
= |
等于 |
<>或!= |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
between … and …. |
两个值之间,等同于 >= and <= |
is null |
为null(is not null 不为空) |
and |
并且 |
or |
或者 |
in |
包含,相当于多个or(not in不在这个范围中) |
not |
not可以取非,主要用在is 或in中 |
like |
like称为模糊查询,支持%或下划线匹配 %匹配任意个字符 下划线,一个下划线只匹配一个字符 |
select ename,sal from emp where sal=5000;
MySQL支持自动类型转换,比如(但是其他数据库不支持,所以不要这么写):
select ename,sal from emp where sal='5000';
select job,ename from emp where job='MANAGER';
这里使用单引号是SQL的标准写法,MySQL也支持使用双引号,开发中只使用单引号
select ename,sal from emp where sal!=5000;
select job,ename from emp where job<>'MANAGER';
select ename,sal from empwhere sal>=1600;
select ename,sal from empwhere sal between 1600 and 3000;
select ename,sal from empwhere sal in (800,1600);
select ename,sal from empwhere job not in (‘MANAGER’,SALESMAN’);
select ename,comm fromemp where comm is null;
null不是数字,是空值,不能进行数学计算,因此不能用=号
select ename from empwhere ename like ‘%s%’; 包含s
select ename from empwhere ename like ‘s%’; s开头
select ename from empwhere ename like ‘_d%’; 第二位为d
select ename from empwhere ename like ‘%n_’; 倒数第二位为n
4、排序
select ename,sal from emporder by sal; 默认asc升序
select ename,sal from emporder by sal desc; 降序
select deptno,ename,salfrom emp order by deptno,sal desc; 多字段排序:先按照deptno升序排列,deptno相同的记录按照sal降序排列
select job,ename,sal fromemp where job=’MANAGER’ order by 3 asc; 通过字段下标排序
5、数据处理函数
Lower |
转换小写 |
upper |
转换大写 |
substr |
取子串(substr(被截取的字符串,起始下标,截取的长度)) |
length |
取长度 |
trim |
去空格 |
str_to_date |
将字符串转换成日期 |
date_format |
格式化日期 |
format |
设置千分位 |
round |
四舍五入 |
rand() |
生成随机数 |
Ifnull |
可以将null转换成一个具体值 |
注意:数据处理函数有些是Mysql特有的,可能在其他数据库不起作用
select lower(ename) as lowername from emp; lower转小写
select upper(ename) as uppername from emp; upper转大写
select substr(ename,1,1)as firstchar from emp; 截取首字母
select substr(ename,3,3)as ename from emp; 取3,4,5个字符
select length(ename) as enameLength from emp; 求字符长度
select * from emp where ename=trim(‘ king ‘); 去掉前后空格(MySQL会自动去掉后面的空格)
select round(123.56); 输出是124
select round(123.56,0); 输出是124
select round(123.56,1); 输出是123.6
select round(123.56,-1); 输出是120
select rand(); 随机数:输出【0,1】区间的随机数
select round(rand()*122); 生成【0,122】区间的任意整数(round用于取整)
case_when_then_else_end函数
示例:匹配工作岗位,为MANAGER时,薪水上调10%,为SALESMAN的时候,薪水上调50%
select ename,job,sal, (case job when 'manager' then sal*1.1 when 'salesman' then sal*1.5 else sal end) as newsal from emp;
注意:经测试,空格多了有时会报错
ifnull函数
select ename,(sal+comm)*12 as yearsal from emp;
select ename,(sal+ifnull(comm,0))*12 as yearsal from emp;
MySQL日期格式:
%Y:代表4位的年份 %y:代表2位的年份
%m:代表月, 格式为(01……12) %c:代表月, 格式为(1……12)
%d :代表日
%H:代表24小时制,格式为(00……23) %h: 代表12小时制,格式为(01……12)
%i : 代表分钟, 格式为(00……59)
%S或%s:代表秒,格式为(00……59)
%r:代表 时间,格式为12 小时(hh:mm:ss [AP]M) %T:代表 时间,格式为24 小时(hh:mm:ss)
select ename,hiredate from emp where hiredate=str_to_date('12-03-1981','%m-%d-%Y'); str_to_date字符串转日期:将字符串按照“月-日-年”的读取方式转换成1981-12-03日期
select ename,date_format(hiredate,'%Y/%m/%d') as hiredate from emp; date_format日期转字符串:以特定格式展示
6、分组/聚合/多行处理函数
count |
取得记录数 |
sum |
求和 |
avg |
取平均 |
max |
取最大的数 |
min |
取最小的数 |
单行函数(如字符串处理函数等)一行输入对应一行输出
多行函数(如上述的聚合函数) 多行输入对应一行输出
注意:
1、聚合函数自动忽略空值,不需要添加where条件排除空值;
2、聚合函数不能直接用在where关键字之后。
select distinct deptno,job from emp order by deptno; 多字段去重 注意:distinct只能出现在所有字段最前面,后面如果有多个字段即为多字段联合去重
select job,max(sal) as maxsal from emp group by job order by maxsal desc; group by分组:计算每个工作岗位的最高薪水,并按从高到低排序
select deptno,job,max(sal) as maxsal from emp group by deptno,job order by deptno,maxsal desc; 计算不同部门不同岗位的最高薪水,并按deptno由低到高、maxsal 由高到低排序
select job,max(sal) as maxsal from emp where job!='manager' group by job; 分组之前过滤:找出除manager以外的工作岗位的最高薪水
select job,avg(sal) as avgsal from emp group by job having avgsal>2000; having分组之后筛选:求每个岗位的平均薪水,要求显示平均薪水>2000的岗位。having必须在group by后面出现,先分组再筛选
select语句总结
一个完整的select语句格式如下
select 字段 from 表名 where ……. group by …….. having …….(就是为了过滤分组后的数据而存在的—不可以单独的出现) order by …….. limit n; |
以上关键字的顺序不能变,严格遵守。
以上语句的执行顺序:
1)from:将硬盘上的表文件加载到内存
2)where:将符合条件的数据行摘取出来,生成一张临时表T
3)group by:根据列中的数据种类,将临时表T划分为多个新的临时表t1、t2....tn
4)having:过滤掉group by中生成的不符合条件的临时表
5)select:对当前的临时表进行整列读取
6)order by:对select生成的临时表进行排序,默认为升序(降序为desc),多字段为联合排序
7)limit:对最终生成的表,截取前面的n条记录
7、跨表查询
left join :以左边为准,返回 左边的所有内容+右边共同内容
right join :同理,以右边为准
(inner ) join :返回共同的内容
年代分类: SQL92 select ename,dname from emp as e,dept as d where e.deptno=d.deptno; SQL99(掌握) select e.ename,d.dname from emp e join dept d on e.deptno=d.deptno where 数据过滤条件; 连接方式分类: 内连接:省略了inner关键字 等值连接 查询员工所对应的部门名称 select e.ename,d.dname from emp as e (inner) join dept as d on e.deptno=d.deptno; 非等值连接 查询员工薪水对应的薪水等级 select ename,sal,grade from emp as e join salgrade as s on e.sal between s.losal and s.hisal; 自连接:把一张表当两张表用 查询员工所对应的领导名称:显示员工和领导的姓名(king没有领导,所以不显示) select a.ename,b.ename as leaderName from emp as a join emp as b on a.mgr=b.empno; 外连接:省略了outer关键字 左连接/右连接 显示所有员工对应的领导名称(king没有领导,以null填充其领导) select a.ename,b.ename as leaderName from emp as a left/right (outer) join emp as b on a.mgr=b.empno; 全连接 查询员工的部门名称,领导名称和薪水等级 select e.ename,d.dname,b.ename as leaderName,s.grade from emp e join dept d on e.deptno=d.deptno join emp b on e.mgr=b.empno join salgrade s on e.sal between s.losal and s.hisal;
等值连接
非等值连接
自连接
左连接
全连接
7、子查询
显示比平均薪资高的员工姓名及薪资
select ename,sal from emp where sal>(select avg(sal) from emp);
找出每个部门的平均薪水,并且显示平均薪水的薪水等级
select deptno,avgsal,grade from (select deptno,avg(sal) as avgsal from emp group by deptno) as t join salgrade as s on t.avgsal between s.losal and s.hisal;
8、其他查询(union、limit)
union:查询岗位为manager和salesman的员工
select ename,job from emp where job='manager' or job='salesman'; select ename,job from emp where job in('manager','salesman'); select ename,job from emp where job='manager' union select ename,job from emp where job='salesman';
limit:只在MySQL中起作用
9、表
DDL数据定义语言:create、drop、alter
1、创建表
create table student( no int(4), name varchar(32), gender char(1) default '1', birth date, email varchar(128) );
create table emp_bak as select * from emp; 复制表
2、删除表
drop table student;
drop table if exists student; MySQL特有
3、修改表
create table student( no int(4), name varchar(32) ); alter table student add email varchar(128); //添加字段 alter table student modify no int(8); //修改字段数据类型 alter table student drop email; //删除字段 alter table student change name username varchar(32); //修改字段名称
alter table student add (c1 char(1),c2 char(1)); //添加多列字段 alter table student change c1 c3 int(1),change c2 c4 char(2); //修改多列字段 alter table student drop c3,drop c4; //删除多列字段
DML数据操控语言:insert,update,delete
3、插入语句
insert into student(no,name,gender,birth,email) values (1,'zhangsan','1','1949-10-1','zhangsan@163.com'); insert into student(no,name,gender,birth,email) values (2,'lisi','0',str_to_date('1949-10-1','%Y-%m-%d'),'lisi@163.com'); insert into student(no,name,gender,birth,email) values (3,'wangwu','0',str_to_date('10-1-1949','%m-%d-%Y'),'wangwu@163.com'); insert into student(no,name,gender) values (4,'lilei','1'); insert into student values (5,'Jerry','1','1959-11-21','Jerry@163.com'); mysql特有,不建议用
4、修改语句
update student set birth='1994-05-10',email='lilei@163.com' where no=4;
5、删除语句
delete from student where no=4; delete from student; //删除表中所有记录
10、约束
常见的约束
a) 非空约束,not null
b) 唯一约束,unique
c) 主键约束,primary key
d) 外键约束,foreign key
e) 自定义检查约束,check(不建议使用)(在mysql中现在还不支持)
create table user( id int(4) PRIMARY key AUTO_INCREMENT, //主键,自增(mysql特有) name varchar(32) not null, //非空 email varchar(128) unique //唯一,不可重复 );
需求:设计数据库表用来存储学生和班级信息
需求分析:
a)学生表student包含:sno,sname,classno,cname
b)一个班级对应多个学生,一对多关系
create table student( id int(4) primary key auto_increment sno int(4) unique, sname varchar(32), classno int(4), cname varchar(32) );
这样建表会造成数据冗余
create table student( sno int(4) primary key auto_increment, sname varchar(32), classno int(4), constraint fk foreign key(classno) references class(cno) //constraint fk:给这个约束起名fk,不需要时可删除。该语句可省略。 字段classno源自class表中的cno ); create table class( cno int(4) primary key, cname varchar(32) );
11、窗口函数
可以把“窗口”这个词想象成一个集合,一个窗口就是一个集合。
over (partition by a order by b) from T 是指:把表T按照a列进行分组,然后,分别让每一个集合的记录按照b列进行排序。
partiton by是可选的。如果不使用partition by,那么就是将整张表作为一个集合,最后使用排序函数得到的就是每一条记录根据b列的排序编号。
在窗口函数中使用SUM、AVG、MAX、MIN计算的范围是排序后每条记录自身和排在它之前的所有记录。
比如按照时间排序,计算各个时间的销售总额,这种统计方法称为累计:
SUM(sale_price)over(partition by 日期 order by 时间) as date_sum
name | subject | score |
zs | math | 75 |
zs | chinese | 81 |
zhaoliu | chinese | 59 |
zhaoliu | math | 16 |
zhaoliu | english | 48 |
wangwu | chinese | 81 |
wangwu | math | 100 |
wangwu | english | 99 |
lisi | math | 89 |
lisi | chinese | 76 |
1、用SQL选出每个人成绩的最高的前两条纪录
select *,row_number() over(partition by name order by score desc) as flag from tmp;
select * from (select *,row_number() over(partition by name order by score desc) as flag from tmp)t where t.flag<=2;
2、查询成绩表中每一科成绩最高的分数以及这个学生的名字,学科名
select *,rank() over(partition by subject order by score desc) as flag from tmp; select name,subject from (select *,rank() over(partition by subject order by score desc) as flag from tmp)t where t.flag=1;
3、学生成绩表,把每科最高分前两名统计出来
select *,row_number() over(partition by subject order by score desc) as flag from tmp; select * from (select *,row_number() over(partition by subject order by score desc) as flag from tmp)t where t.flag<=2;
4、找出单科成绩高于该科平均成绩的同学名单(无论该学生有多少科,只要有一科满足即可)
传统方法
select subject,avg(score) from tmp group by subject; select tmp.name,tmp.subject,tmp.score,t.avg_score from tmp join (select subject,avg(score) as avg_score from tmp group by subject)t on tmp.subject=t.subject where tmp.score>t.avg_score;
窗口函数
select *,avg(score) over(partition by subject) as flag from tmp; select * from (select *,avg(score) over(partition by subject) as flag from tmp)t where t.score>t.flag;
5、每个用户按照订单id,截止到当前的累计订单金额/平均订单金额/最大订单金额/最小订单金额/订单数是多少?
select * from (select order_id,user_no,amount,create_date, sum(amount) over w as sum1, avg(amount) over w as avg1, max(amount) over w as max1, min(amount) over w as min1, count(amount) over w as count1 from order_tab WINDOW w AS (partition by user_no order by order_id ) ) t;
12、经典面试题
1、取得每个部门最高薪水的人员名字
第一步,求出每个部门的最高薪水
select deptno,max(sal) as maxsal from emp group by deptno;
第二步,将上述查询当成临时表t,进行表连接查询
select e.deptno,e.ename,t.maxsal,e.sal from emp e join (select deptno,max(sal) as maxsal from emp group by deptno) t on e.deptno=t.deptno where t.maxsal=e.sal order by e.deptno;
2、哪些人的薪水在平均薪水之上
第一步,先求出每个部门的平均薪水
select deptno,avg(sal) as avgsal from emp group by deptno;
第二步,将上述查询当成临时表t,进行表连接查询
select e.deptno,e.ename,e.sal,t.avgsal from emp e join (select deptno,avg(sal) as avgsal from emp group by deptno)t on e.deptno=t.deptno where e.sal>t.avgsal order by e.deptno;
3、取得部门中所有人平均的薪水等级
select e.deptno,e.ename,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal order by e.deptno;
select t.deptno,avg(t.grade) as avgGrade from (select e.deptno,e.ename,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal order by e.deptno) t group by t.deptno;
4、平均薪水最高的部门编号
第一步,先求出每个部门的平均薪水
select deptno,avg(sal) as avgsal from emp group by deptno;
第二步,将上述查询当成临时表t(deptno,avgsal),进行子查询,得到最高平均薪水
select max(t.avgsal) from (select deptno,avg(sal) as avgsal from emp group by deptno) t;
第三步,having筛选
select deptno,avg(sal) as avgsal from emp group by deptno having avgsal= (select max(t.avgsal) from (select deptno,avg(sal) as avgsal from emp group by deptno) t) ;
5、平均薪水最高的部门名称
select e.deptno,d.dname,avg(e.sal) as avgsal from emp e join dept d on e.deptno=d.deptno group by deptno having avgsal=(select max(t.avgsal) from (select deptno,avg(sal) as avgsal from emp group by deptno) t);
6、取得比普通员工(员工代码没有在mgr上出现的)的最高薪水还要高的经理人姓名
第一步,找出普通员工
select * from emp where empno not in (select distinct mgr from emp where mgr is not null);
第二步,找出普通员工的最高薪水
select max(sal) as maxsal from emp where empno not in (select distinct mgr from emp where mgr is not null);
第三步,子查询
select ename from emp where sal>(select max(sal) as maxsal from emp where empno not in (select distinct mgr from emp where mgr is not null)) ;
7、取得薪水最高的前五名员工,第六到第十的员工,最后入职的5名员工
select * from emp order by sal desc limit 5; //前5 select * from emp order by sal desc limit 5,5; //第6到第10 select * from emp order by hiredate desc limit 5;
8、每个薪水等级有多少员工
第一步,先求出每个员工的薪水等级
select e.ename,e.sal,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal;
第二步,将以上结果当成临时表t(ename,sal,grade),进行子查询
select t.grade,count(*) from (select e.ename,e.sal,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal)t group by t.grade;
9、列出所有员工及领导名字
select e.ename,t.ename as LeaderName from emp e left join emp t on e.mgr=t.empno;
10、列出受雇日期早于其领导的员工编号、姓名、部门(多表连接)
select e.empno,e.ename,e.hiredate,t.hiredate as leaderDate,d.dname //hiredate可省略,仅为了直观观察 from emp e join emp t on e.mgr=t.empno join dept d on e.deptno=d.deptno where e.hiredate<t.hiredate;
11、列出至少有5个员工的部门(having对分组后的结果进行筛选)
select deptno,count(ename) as totalEmp from emp group by deptno having totalEmp>=5;
12、列出所有‘CLERK’的姓名及其部门名称、部门人数
第一步,求出CLERK姓名及部门名称,令查询结果为t1
select d.deptno,d.dname,e.ename from emp e join dept d on e.deptno=d.deptno where e.job='CLERK';
第二步,求出每个部门的员工数量,令查询结果为t2
select deptno,count(*) as numEmp from emp group by deptno;
第三步,内连接合并
select t1.ename,t1.dname,t2.numEmp from (select d.deptno,d.dname,e.ename from emp e join dept d on e.deptno=d.deptno where e.job='CLERK')t1 join (select deptno,count(*) as numEmp from emp group by deptno)t2 on t1.deptno=t2.deptno;
13、列出最低薪水大于1500的各种工作,及从事此工作的全部人数
select job,min(sal) as minsal,count(ename) as empNum from emp group by job having minsal>1500;
14、列出薪水高于平均薪水的所有员工、所在部门、上级领导、工资等级
第一步,求出平均薪水
select avg(sal) from emp;
第二步,多表连接
select e.ename,d.dname,e2.ename as leaderName,s.grade from emp e join dept d on e.deptno=d.deptno left join emp e2 on e2.empno=e.mgr join salgrade s on e.sal between s.losal and s.hisal where e.sal>(select avg(sal) from emp);
15、列出每个部门的员工数量、平均工资和平均工作期限
select d.dname,count(e.ename) as empNum,avg(e.sal) as avgsal,avg((to_days(now())-to_days(e.hiredate))/365) as avgworkYear from emp e join dept d on e.deptno=d.deptno group by e.deptno;
16、列出各种工作的最低薪水及从事该工作的员工姓名
第一步,求出各工作的最低薪水
select job,min(sal) as minsal from emp group by job;
第二步,将以上查询结果令为t表(job,minsal),进行表连接
select e.job,e.ename,e.sal from emp e join (select job,min(sal) as minsal from emp group by job)t on e.job=t.job where e.sal=t.minsal;
17、列出所有员工的年工资,按年薪从低到高排序
考察 ifnull 的用法
IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。
18、给任职时日超过30年的员工加薪10%
create table emp_bak as select * from emp; update emp_bak set sal=sal*1.1 where (to_days(now())-to_days(hiredate))>30;
create table s( sno int(4) primary key auto_increment, sname varchar(32) ); create table c( cno int(4) primary key auto_increment, cname varchar(32), cteacher varchar(32) ); create table sc( sno int(4), cno int(4), scgrade double(3,1), //3位有效数字,1位小数 constraint sc_sno_cno_pk primary key(sno,cno), constraint sc_sno_fk foreign key(sno) references s(sno), constraint sc_cno_fk foreign key(cno) references c(cno) );
插入语句
insert into s(sname) values('zhangsan'); insert into s(sname) values('lisi'); insert into s(sname) values('wangwu'); insert into s(sname) values('zhaoliu'); insert into c(cname,cteacher) values('java','吴老师'); insert into c(cname,cteacher) values('c++','王老师'); insert into c(cname,cteacher) values('Python','张老师'); insert into c(cname,cteacher) values('MySQL','郭老师'); insert into c(cname,cteacher) values('Oracle','黎明'); insert into sc values(1,1,30); insert into sc values(1,2,50); insert into sc values(1,3,80); insert into sc values(1,4,90); insert into sc values(1,5,70); insert into sc values(2,2,80); insert into sc values(2,3,50); insert into sc values(2,4,70); insert into sc values(2,5,80); insert into sc values(3,1,60); insert into sc values(3,2,70); insert into sc values(3,3,80); insert into sc values(4,3,50); insert into sc values(4,4,80);
1、找出没选过黎明老师的所有学生姓名
第一步,黎明老师的授课编号
select cno from c where cteacher='黎明';
第二步,选过黎明老师的学生编号
select sno from sc where cno=(select cno from c where cteacher='黎明');
第三步,子查询反推
select sname from s where sno not in(select sno from sc where cno=(select cno from c where cteacher='黎明'));
2、列出2门及以上不及格学生姓名及其平均成绩
第一步,求2门及以上不及格的学生姓名,令为t1
select sc.sno,sname,count(*) as failNum from sc join s on s.sno=sc.sno where scgrade<60 group by sno having failNum>=2;
第二步,求各学生的平均分,令为t2
select sno,avg(scgrade) as avgScore from sc group by sno;
第三步,合并两表
select t1.sname,t2.avgScore from (select sc.sno,sname,count(*) as failNum from sc join s on s.sno=sc.sno where scgrade<60 group by sno having failNum>=2)t1 join (select sno,avg(scgrade) as avgScore from sc group by sno)t2 on t1.sno=t2.sno;
3、既学过1号课又学过2号课的所有学生姓名
第一步
select sno from sc where cno=1; select sno from sc where cno=2;
第二步,合并
select sno from sc where cno=1 and sno in (select sno from sc where cno=2);
第三步,表连接
select sname from sc join s on sc.sno=s.sno where sc.cno=1 and sc.sno in (select sc.sno from sc where sc.cno=2);