学习分篇目标
进阶篇地址:http://t.csdn.cn/YYfaP
关系型数据库(RDBMS)
概念:建立在关系模型基础上,由多张相互连接的二维表组成的数据库。
而所谓二维表,指的是由行和列组成的表,里面都是基于二维表存储数据的。简单说,基于二维表存储数据的数据库就称为关系型数据库,不是基于二维表存储数据的数据库,就是非关系型数据库。
1). SQL语句可以单行或多行书写,以分号结尾。
2). SQL语句可以使用空格/缩进来增强语句的可读性。
3). MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。
4). 注释:
分类 | 全称 | 说明 |
---|---|---|
DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库,表,字段) |
DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 |
DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
数据定义语言,用来定义数据库对象(数据库,表,字段)
1). 查询所有数据库
show databases ;
2). 查询当前数据库
select database() ;
3). 创建数据库
create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序规则 ] ;
4). 删除数据库
drop database [ if exists ] 数据库名 ;
5). 切换数据库
use 数据库名 ;
1). 查询当前数据库所有表
show tables;
2). 查看指定表结构
desc 表名 ;
3). 查询指定表的建表语句
show create table 表名 ;
4). 创建表结构
comment是写字段注释的,写的时候不加[ ]。这个是可选的,注释可写可不写
CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1注释 ],
字段2 字段2类型 [COMMENT 字段2注释 ],
字段3 字段3类型 [COMMENT 字段3注释 ],
…
字段n 字段n类型 [COMMENT 字段n注释 ]
) [ COMMENT 表注释 ] ;
MySQL中的数据类型有很多,主要分为三类:数值类型、字符串类型、日期时间类型。
1). 数值类型
1). 年龄字段 -- 不会出现负数, 而且人的年龄不会太大
age tinyint unsigned
2). 分数 -- 总分100分, 最多出现一位小数
score double(4,1)
2). 字符串类型
char 与 varchar 都可以描述字符串,char是定长字符串,指定长度多长,就占用多少个字符,和字段值的长度无关 。而varchar是变长字符串,指定的长度为最大占用长度 。相对来说,char的性能会更高些
1). 用户名 username ------> 长度不定, 最长不会超过50
username varchar(50)
2). 性别 gender ---------> 存储值, 不是男,就是女
gender char(1)
3). 手机号 phone --------> 固定长度为11
phone char(11)
3). 日期时间类型
1). 生日字段 birthday
birthday date
2). 创建时间 createtime
createtime datetime
表操作-案例
设计一张员工信息表,要求如下:
建表语句如下
create 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 '身份证号',
entrydate date comment '入职时间'
) comment '员工表';
1). 添加字段
ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
2). 修改数据类型
ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度);
3). 修改字段名和字段类型
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
4). 删除字段
ALTER TABLE 表名 DROP 字段名;
5). 修改表名
ALTER TABLE 表名 RENAME TO 新表名;
1). 删除表
DROP TABLE [ IF EXISTS ] 表名;
2). 删除指定表, 并重新创建表
TRUNCATE TABLE 表名;
注意: 在删除表的时候,表中的全部数据也都会被删除
添加数据
1). 给指定字段添加数据
INSERT INTO 表名 (字段名1, 字段名2, …) VALUES (值1, 值2, …);
2). 给全部字段添加数据
INSERT INTO 表名 VALUES (值1, 值2, …);
3). 批量添加数据
INSERT INTO 表名 (字段名1, 字段名2, …) VALUES (值1, 值2, …), (值1, 值2, …), (值
1, 值2, …) ;
INSERT INTO 表名 VALUES (值1, 值2, …), (值1, 值2, …), (值1, 值2, …) ;
注意事项:
• 插入数据时,指定的字段顺序需要与值的顺序是一一对应的。
• 字符串和日期型数据应该包含在引号中。
• 插入的数据大小,应该在字段的规定范围内。
修改数据
修改数据的具体语法为:
UPDATE 表名 SET 字段名1 = 值1 , 字段名2 = 值2 , … [ WHERE 条件 ] ;
注意事项:
修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据。
删除数据
删除数据的具体语法为:
DELETE FROM 表名 [ WHERE 条件 ] ;
注意事项:
• DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据。
• DELETE 语句不能删除某一个字段的值(可以使用UPDATE,将该字段值置为NULL即可)。
• 当进行删除全部数据操作时,datagrip会提示我们,询问是否确认删除,我们直接点击Execute即可。
在基本查询的DQL语句中,不带任何的查询条件
1). 查询多个字段
SELECT 字段1, 字段2, 字段3 … FROM 表名 ;
SELECT * FROM 表名 ;
注意 : * 号代表查询所有字段,在实际开发中尽量少用(不直观、影响效率)。
2). 字段设置别名
SELECT 字段1 [ AS 别名1 ] , 字段2 [ AS 别名2 ] … FROM 表名;
3). 去除重复记录
SELECT DISTINCT 字段列表 FROM 表名;
查询年龄等于18 或 20 或 40 的员工信息
select * from emp where age = 18 or age = 20 or age =40;
select * from emp where age in(18,20,40);
查询姓名为两个字的员工信息 _ %
select * from emp where name like '__';
注意 : NULL值是不参与所有聚合函数运算的。
语法:
SELECT 聚合函数(字段列表) FROM 表名 ;
例如
查总条数 select count(*) from 表名;
查年龄最大员工 select max(age) from 表名;
语法
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后过滤条件 ];
where与having区别
例如下
查询年龄小于45的员工 , 并根据工作地址分组 , 获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from emp
where age < 45 group by workaddress having address_count >= 3;
注:这里的count( * ) address_count是起别名,给count起别名为address_count
1、执行顺序是where > 聚合函数 > having
2、先用where把数据过滤了,再用count(*)把过滤出来的所有数据进行统计
3、count统计出来结果之后再通过having对这个结果进行二次过滤
语法
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1 , 字段2 排序方式2 ;
注意事项:
• 如果是升序, 可以不指定排序方式ASC ;
• 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序 ;
例如下
根据年龄对公司的员工进行升序排序 , 年龄相同 , 再按照入职时间进行降序排序
select * from emp order by age asc , entrydate desc;
语法
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数 ;
注意事项:
• 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数。
• 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。
• 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。
案例
查询第2页员工数据, 每页展示10条记录 --------> (页码-1)*页展示记录数
select * from emp limit 10,10;
第一个参数:从哪个索引位置开始查 ==>(页码-1)* 每页展示记录数
第二个参数:查多少条
总结案例
查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
select * from emp where gender = '男' and age between 20 and 40
order by age asc , entrydate asc limit 5 ;
查询用户
select * from mysql.user;
Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问,是不可以
远程访问的。 User代表的是访问该数据库的用户名。在MySQL中需要通过Host和User来唯一标识一
个用户。
创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码' ;
删除用户
DROP USER '用户名'@'主机名' ;
注意事项:
• 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户
• 主机名可以使用 % 通配。
• 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用。
案例
修改用户的访问密码为1234;
alter user ‘用户名’@‘%’ identified with mysql_native_password by ‘1234’;
1). 查询权限
SHOW GRANTS FOR ‘用户名’@‘主机名’ ; 1
2). 授予权限
GRANT 权限列表 ON 数据库名.表名 TO ‘用户名’@‘主机名’;
3). 撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM ‘用户名’@‘主机名’;
注意事项:
函数 是指一段可以直接被另一段程序调用的程序或代码。MySQL中的函数主要分为以下四类: 字符串函数、数值函数、日期函数、流程函数。
MySQL中内置了很多字符串函数
用法如下
concat : 字符串拼接
select concat('Hello' , ' MySQL');
lower : 全部转小写
select lower('Hello');
upper : 全部转大写
select upper('Hello');
lpad : 左填充
select lpad('01', 5, '-');
结果为 - - -01
rpad : 右填充
select rpad('01', 5, '-');
结果为 01- - -
trim : 去除空格
select trim(' Hello MySQL ');
substring : 截取子字符串
select substring('Hello MySQL',1,5);
重点案例
企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员工的工号应该为00001。
update emp set workno = lpad(workno, 5, '0');
ceil:向上取整 只要小数位有数就进一
select ceil(1.1);
floor:向下取整 不管小数位是多少都舍去
select floor(1.9);
mod:取模 7%4 = 3
select mod(7,4);
rand:获取随机数 获取0到1之间的随机小数
select rand();
round:四舍五入 第二个参数是指保留几位小数
select round(2.344,2);
经典案例
通过数据库的函数,生成一个六位数的随机验证码。
思路: 获取随机数可以通过rand()函数,但是获取出来的随机数是在0-1之间的,所以可以在其基础上乘以1000000,然后舍弃小数部分,如果长度不足6位,补0
select lpad(round(rand()*1000000 , 0), 6, '0');
注意:这里datediff中,是按date1-date2来计算天数的,所以date1是大的日期,date2是小的日期
curdate:当前日期
select curdate();
curtime:当前时间
select curtime();
now:当前日期和时间
select now();
YEAR , MONTH , DAY:当前年、月、日
select YEAR(now());
select MONTH(now());
select DAY(now());
date_add:增加指定的时间间隔
select date_add(now(), INTERVAL 70 YEAR );
datediff:获取两个日期相差的天数
select datediff(‘2022-1-01’, ‘2021-12-01’);
案例:
查询所有员工的入职天数,并根据入职天数倒序排序。
思路: 入职天数,就是通过当前日期 - 入职日期,所以需要使用datediff函数来完成。
select name datediff(curtdate(), entrydate) as entrytime
from emp order by entrytime desc;
if 条件判断,第一个参数为判读条件,满足,返回第二个,不满足返回第三个
select if(false, 'Ok', 'Error');
ifnull 只有为null的时候,才会返回默认值default
select ifnull('Ok','Default');
select ifnull('','Default'); //这里返回的是空串
select ifnull(null,'Default');
case when then else end 相当与if-else这种分支判断
需求: 查询emp表的员工姓名和工作地址 (北京/上海 ----> 一线城市 , 其他 ----> 二线城市)
select
name,
( case workaddress when '北京' then '一线城市' when '上海' then '一线城市'
else'二线城市' end ) as '工作地址'
from emp;
经典案例
案例:统计班级各个学员的成绩,展示的规则如下:
大于等于85,展示优秀
大于等于60,展示及格否则,展示不及格
select
id,
name,
(case when math >= 85 then '优秀' when math >=60 then '及格' else '不及格' end )'数学',
(case when english >= 85 then '优秀' when english >=60 then '及格' else '不及格'end ) '英语',
(case when chinese >= 85 then '优秀' when chinese >=60 then '及格' else '不及格'end ) '语文'
from score;
注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。
注意:设置id自增后,假如现在id为1,即使插入数据失败,也从数据库申请了自增,再插入新数据时,id就为3了,是不连续的
添加外键,一种是建表时添加外键,一种是建表后添加外键
CREATE TABLE 表名(
字段名 数据类型,
…
[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名)
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名)REFERENCES 主表 (主表列名) ;
例如
为emp表的dept_id字段添加外键约束,关联dept表的主键id。
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
例如
删除emp表的外键fk_emp_dept_id。
alter table emp drop foreign key fk_emp_dept_id;
外键删除更新行为
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名 (主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;
例如下
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id)
on update cascade on delete cascade ;
内连接查询的是两张表交集部分的数据。
SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 … ;
查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)
表结构: emp , dept
连接条件: emp.dept_id = dept.id
select emp.name , dept.name from emp , dept where emp.dept_id = dept.id ;
-- 为每一张表起别名,简化SQL编写,但是起了别名就得用别名
select e.name,d.name from emp e , dept d where e.dept_id = d.id;
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 … ;
查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现)
表结构: emp , dept
连接条件: emp.dept_id = dept.id
select e.name, d.name from emp e join dept d on e.dept_id = d.id;
RIGHT 指出的是 OUTER JOIN 右边的表,lEFT 指出的是 OUTER JOIN 左的表。
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;
左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集部分的数据。
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;
右外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。
案例
查询emp表的所有数据, 和对应的部门信息
由于需求中提到,要查询emp的所有数据,所以是不能内连接查询的,需要考虑使用外连接查询。
表结构: emp, dept
连接条件: emp.dept_id = dept.id
select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id;
查询dept表的所有数据, 和对应的员工信息(右外连接)
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次。我们先来学习一下自连接的查询语法:
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
而对于自连接查询,可以是内连接查询,也可以是外连接查询。
就把该表看成两张表
注意事项:
在自连接查询中,必须要为表起别名,要不然我们不清楚所指定的条件、返回的字段,到底是哪一张表的字段。
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;
注意:
如果多条查询语句查询出来的结果,字段数量不一致,在进行union/union all联合查询时,将会报错
SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 );
根据子查询结果不同,分为:
A. 标量子查询(子查询结果为单个值)
B. 列子查询(子查询结果为一列)
C. 行子查询(子查询结果为一行)
D. 表子查询(子查询结果为多行多列)
标量子查询
子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子询。
常用的操作符:=, <>, >, >=, < , <=
案例
查询 “销售部” 的所有员工信息
select * from emp where dept_id = (select id from dept where name = '销售部');
列子查询
子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。
常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL
案例
查询比 财务部 所有人工资都高的员工信息
①. 查询所有 财务部 人员工资
select id from dept where name = '财务部';
select salary from emp where dept_id = (select id from dept where name = '财务部');
②. 比 财务部 所有人工资都高的员工信息
select * from emp where salary > all ( select salary from emp where dept_id =(select id from dept where name = '财务部') );
行子查询
子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。
常用的操作符:= 、<> 、IN 、NOT IN
案例:
查询与 “张无忌” 的薪资及直属领导 相同的员工信息 ;
select * from emp where (salary,managerid) = (select salary, managerid from emp where name = '张无忌');
把where salary = … and managerid = … 给转化为 where (salary, managerid) = (…)
表子查询
子查询返回的结果是多行多列,这种子查询称为表子查询。
常用的操作符:IN
案例
查询与 “鹿杖客” , “宋远桥” 的职位和薪资相同的员工信息
select * from emp where (job,salary) in ( select job, salary from emp where name ='鹿杖客' or name = '宋远桥' );
查询入职日期是 “2006-01-01” 之后的员工信息 , 及其部门信息。把子查询结果作为新表,进行左外连接,通过外键查到dept表中信息
select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id ;
这些问题纯当练手习题,掌握这些,基本查询业务都没啥问题了
答案如下
select e.name , e.age , e.job , d.name from emp e , dept d where e.dept_id = d.id;
select e.name , e.age , e.job , d.name from emp e inner join dept d on e.dept_id =d.id where e.age < 30;
select distinct d.id , d.name from emp e , dept d where e.dept_id = d.id;
select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age >40 ;
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary between s.losal and s.hisal;
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 = '研发部';
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发部';
select * from emp where salary > ( select salary from emp where name = '灭绝' );
select * from emp where salary > ( select avg(salary) from emp );
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id );
select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数' from dept d;
select s.name , s.no , c.name from student s , student_course sc , course c where s.id = sc.studentid and sc.courseid = c.id ;
事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
就比如: 张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000。 这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。
我们需要务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。
注意: 默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务。
1). 查看/设置事务提交方式
SELECT @@autocommit ;
查看事务,1为自动,0为手动
SET @@autocommit = 0
设置事务
2). 提交事务
COMMIT;
3). 回滚事务
ROLLBACK;
注意:上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提
交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交
控制事务二
开启事务
START TRANSACTION 或 BEGIN ;
提交事务
COMMIT;
回滚事务
ROLLBACK;
上述就是事务的四大特性,简称ACID。
在一个事务中,先查询数据结果为空,另一个事务此时又插入数据并提交了,造成本事务再插入数据结果失败,由于解决不可重复读的缘故,结果必须相同,再查询结果也是空,这就是幻读。
× 代表没有 勾代表有
Serializable 最后的隔离级别加了锁,用锁来解决并发问题,一个事务操作,其他的事务等待
查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
注:session是当前会话有效,global是全局有效 后面的{…}中是事务隔离级别
注意:事务隔离级别越高,数据越安全,但是性能越低。