Mysql基础篇-查询、函数、多表、事务
1. 基础篇
1.1 mysql用户和权限管理
查询用户
USER mysql;
SELECT * FROM user;
创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
例如:
-- 创建用户cc,只能在当前主机localhost访问,密码1234
create user 'cc'@'localhost' identified by '1234';
--创建用户gg,可以再任意主机访问改数据库,密码
create user 'gg'@'%' identified by '1234';
修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'
删除用户
DROP USER '用户名'@'主机名'
- 权限管理
查询权限
show grants for '用户名'@'主机名';
授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名'
例如给cc授予权限
grant all on testDb.table1 to 'cc'@'%';
撤销权限
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名'
1.2 执行顺序
- 查询语句执行顺序问题:
验证:根据执行顺序,以下会报错
select user_name un from ussers where un = 'tom'
首先执行 From 表名列表操作
然后执行 where 条件列表 来指定查询条件
接着执行 Group By和Having 指定分组和分组之后的条件
第四步执行 Select 来决定查询返回哪些子段
后面再分别执行 Order By 和 Limi
- 聚合函数
| 函数 | 功能 |
| ------------ | ------------ |
| count |统计数量 |
| max | 最大值 |
| min | 最小值 |
| avg | 求平均 |
| sum | 求和 |
注意:Null值不参与所有的聚合函数运算
1.3 order_by 问题
列的别名只能在ORDER_BY中使用,不能再WHERE中使用,如下会报错
SELECT SELECT employee_id,salary,salary * 12 annual_sal
FROM employees
WHERE annual_sal > 81600;
1.4 二级排序
- 可以使用不在select列表中的列排序
- 在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序,如果第一列数据中所有的值都是唯一的,将不再对第二列进行排序
1.5 笛卡尔积(交叉连接)理解
笛卡尔积是一个数学运算,假设有两个集合X和Y,那么X和Y的笛卡尔积就是X和Y的所有可能组合,也就是第一个对象来自于X,第二个对象来自于Y的所有可能。组合的个数为两个集合中元素个数的乘积数。
- sql92中笛卡尔积成为交叉连接英文为CROSS JOIN。在SQL99中也是使用CROSS JOIN表示交叉连接,它的作用就是把任意表进行连接,即使两张表不相关,以下的情况会出现笛卡尔积现象:
#查询员工姓名和所在部门名称
SELECT last_name,department_name FROM employees,departments;
SELECT last_name,department_name FROM employees CROSS JOIN departments;
SELECT last_name,department_name FROM employees INNER JOIN departments;
SELECT last_name,department_name FROM employees JOIN departments;
笛卡尔积的错误会在下面条件下产生:
- 省略多个表的连接条件(或关联条件)
- 连接条件(或关联条件)无效
- 所有表中的所有行相互连接
避免,在where加入有效的连接条件SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;
1.5.1 内连接查询
内连接查询的是两张表交集部分
隐式内连接:
SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;
显式内连接
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;
显示性能比隐式高
例如:
-- 查询员工姓名,及关联的部门的名称
-- 隐式
select e.name, d.name from employee as e, dept as d where e.dept = d.id;
-- 显式
select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;
1.5.2 外连接查询
左外连接:
查询左表所有数据,一级两张表交集部分数据
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;
想当于查询表1的所有数据,包含表1和表2交集部分数据
右外连接
查询右表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;
例如:
-- 左
select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;
select d.name, e.* from dept d left outer join emp e on e.dept = d.id; -- 这条语句与下面的语句效果一样
-- 右
select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;
1.5.3自连接查询
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;
自连接查询,可以是内连接查询,也可以是外连接查询
例如:
-- 查询员工及其所属领导的名字
select a.name, b.name from employee a, employee b where a.manager = b.id;
-- 没有领导的也查询出来
select a.name, b.name from employee a left join employee b on a.manager = b.id;
1.5.5 联合查询 union
把多次查询的结果合并,形成新的查询集
语法:
SELECT 字段列表 FROM 表A ...
UNION [ALL]
SELECT 字段列表 FROM 表B ...
注意事项:
- UNION ALL会有重复结果,UNION 不会
- 联合查询比使用or效率高,不会使索引失效
1.5.6 子查询
嵌套查询也称为子查询
子查询的外部的语句可以是INSERT/UPDATE/DELETE/SELECT中的任何一个
根据子查询结果可以分为
- 标量子查询 (子查询的结果为单个值)
- 列子查询(子查询的结果为一列)
- 行子查询(子查询的结果为一行)
- 表子查询(子查询的结果为多行多列)
(1)列子查询
子查询返回的结果为一列(可以是多行)
常用的操作符
| 操作符 |描述 |
| ------------ | ------------ |
| IN | 在指定的集合范围内,多选一 |
| NOT IN | 不在指定的集合范围内 |
| ANY |子查询返回列表中,有任意一个满足即可 |
| SOME |与ANY等同,使用some的地方都可以使用any|
|ALL | 子查询返回列表的所有值都必须满足 |
例子:
-- 查询销售部和市场部的所有员工信息
select * from employee where dept in (select id from dept where name = '销售部' or name = '市场部');
-- 查询比财务部所有人工资都高的员工信息
select * from employee where salary > all(select salary from employee where dept = (select id from dept where name = '财务部'));
-- 查询比研发部任意一人工资高的员工信息
select * from employee where salary > any (select salary from employee where dept = (select id from dept where name = '研发部'));
(2)行子查询
返回的结果是一行(可以是多列)
常用操作符:= ,<,>,IN, NOT IN,exists
例子:
-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);
select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');
select department_name from departments d where EXITS(
select * from employees e where e.department_id = d.department_id
)
1.6 函数
1.6.1 字符串函数
函数 | 功能 |
---|---|
CONCAT(S1,S2,...,Sn) | 字符串拼接,将S1,S2...Sn拼接成一个字符串 |
LOWER(str) | 将字符串str全部转为小写 |
UPPER(str) | 将字符串全部转为大写 |
LPAD(str,n,pad) | 左填充,用字符串pad对str的左边进行填充,达到n |
个字符串长度 | |
RPAD(str,n,pad) | 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度 |
TRIM(str) | 去掉字符串头部和尾部的空格 |
SUBSTRING(str,start,len) | 返回从字符串str的start位置起的len长度字符串 |
例如: |
-- 切片(起始索引为1)
SELECT SUBSTRING('Hello World', 1, 5);
1.6.2 数值函数
函数 | 功能 |
---|---|
CEIL(x) | 向上取整 |
FLOOR(x) | 向下取整 |
MOD(x,y) | 返回x/y的模 |
RAND() | 返回0-1内的随机数 |
ROUND(x,y) | 求参数x的四舍五入值,保留y位小数 |
1.6.3 日期函数
函数 | 功能 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
YEAR(date) | 获取指定date的年份 |
MONTH(date) | 获取指定date的月份 |
DAY(date) | 获取指定date的日期 |
DATE_ADD(date,INTERVAL expr type) | 返回一个日期/时间值加上一个时间间隔expr后的时间值 |
DATEDIFF(date1,date2) | 返回起始时间date1和结束时间date2之间的天数 |
例如:
--查询所员工的入职天数。并根据入职天数倒叙排序
select name,datediff(curdate(),entryDate) as 'entrydays' from emp order by entrydays desc;
1.6.4 流程函数
函数 | 功能 |
---|---|
IF(value,t,f) | 如果value为true,则返回t,否则返回f |
IFNULL(value1,value2) | 如果vaule1不为空,返回value1,否则返回value2 |
CASE WHEN[val1] THEN[res1] ... ELSE[default] END | 如果val1为true,返回res1,...否则返回default默认值 |
CASE[expr] WHEN [val1] THEN [res1] ... ELSE[default] END | 如果expr的值等于val1,返回res1,...否则返回default默认值 |
例如: |
select
name,
(case when age > 30 then '中年' else '青年' end
) ageLevel
from
emp;
select name,
(case wokeAddr when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end) as '工作地址' from emp;
1.7 事务
隐式事务:没有明显的开启和结束,本身就是一条事务可以自动提交,如insert、update、delete
显式事务:具有明显的开启和结束,例如以下格式
1、开启事务
set autocommit=0;#关闭自动提交
start transaction;#开启事务机制
2、编写一组逻辑sql语句
注意:sql语句支持的是insert、update、delete
【设置回滚点,可选项】
savepoint 回滚点名;
3、结束事务
提交:commit;
回滚:rollback;
回滚到指定的地方: rollback to 回滚点名;
1.7.1并发事务问题:
(1)脏读:一个事务读到另一个事务还没提交的数据
事务A执行到第二更新了id为1的数据,事务B此时查询,但是事务A还没有执行完提交。导致脏读
(2)不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
事务A第一次查询后,第二步事务B进行了更新数据病提交,事务A继续查询和第一次查询的结果不一致
(3)幻读
一个事务按照条件查询数据时,没有对应的数据航,但是插入数据时,发现这行数据已经存在,好像出现幻影
事务A第一次查询没有对应数据,事务B插入一条数据,接着查询A能查到
解决以上问题,隔离级别
- √表示在当前隔离级别下该问题会出现
- Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差
查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
SESSION是会话级别,表示只针对当前会话有效,GLOBAL表示对所有会话有效
1.7.2 丢失更新(写问题)
一、定义
在事务的隔离级别内容中,能够了解到两个不同的事务在并发的时候可能会发生数据的影响。脏读、不可重复读,幻读三个问题都是事务A对数据进行修改、增加,事务B总是在做读操作。如果两事务都在对数据进行修改则会导致另外的问题,丢失更新。
解决:
(1)悲观锁:认为两个事务更新操作一定会发生丢失更新
解决:通过在语句后面添加 for update来实现行级上锁,所以又称为行级锁。例如:select * from t_account t where id = 'id' for update;
(2)乐观锁:认为事务不一定会产生丢失更新,让事务进行并发修改,不对事务进行锁定
解决:由程序员自己解决,可以通过给数据表添加自增的version或时间戳timestamp,进行数据修改时,数据库会检测version字段或者时间戳是否与原来的一致,若不一致,抛出异常或重新查询