Mysql是工作中经常用到的关系型数据库管理系统, 在这里做个学习笔记的总结, 篇幅较长, 采用持续更新的方式.
//2019/01/27 更新=========================>>>>
安装
基于CentOS7.3的安装方式
● 下载rpm包: wget https://dev.mysql.com/get/mysql80-community-release-el7-2.noarch.rpm
● 将yum源存进yum列表: rpm -Uvh
● 进入yum列表: cd /etc/yum.repos.d //该目录下有mysql-community.repo 和 mysql-community-source.repo
● 编辑mysql-community.repo文件: vim mysql-community.repo //将需要的版本的enabled的值字段改为1, 不需要的版本改为0 只能有一个enabled字段的值为1
● 安装Mysql服务器版: yum install mysql-community-server //会自动安装客户端版
● 配置编码和表名大小写信息: vim /etc/my.cnf ==> 写上如下蓝色字体内容:
[mysqld]
character-set-server=utf8 //设置数据库默认编码为utf8
lower_case_table_names=1 //不区分表名大小写
● 启动 / 停止Mysql: systemctl start mysqld.service / systemctl stop mysqld.service
使用
● 常用命令(在Mysql客户端内执行的命令)
○ 查询数据库编码: SHOW VARIABLES LIKE '%char%';
○ 查询数据库当前时区: SHOW VARIABLES LIKE 'time_zone';
○ 设置时区: SET time_zone='+9:00'; # 设置变量值, 将时区设置为东9区
○ 设置密码: mysqladmin -u root password "<自定义密码>"
○ 启动mysql数据库后, 自然就是登录数据库: mysql -h <主机名> -P <端口号> -u root -p <密码>
★ -h: 主机名, 本机可不写, 中间空格可有可无
★ -P: 端口号, 大写P, 默认3306可不写, 中间空格可有可无
★ -u: 用户名, 一般就是root, 中间空格可有可无
★ -p: 密码, 设置的用户密码, 中间必须无空格, 或者密码不写, 回车后再输入密码(用于隐式输入)
○ 查询Mysql服务器版本: select version();
○ 查询所有库: show databases; //注意需要分号
○ 进入指定库: use <数据库名>;
○ 查询当前库所有表: show tables;
○ 查询指定表的表结构: desc <表名>;
○ 查询其他库所有表: show tables from <其他数据库名>
○ 查询当前位于哪个库: select database();
● 语法规范
○ 不区分大小写, 建议关键字大写, 表名 / 列名小写
○ 每条命令用分号结尾(或者\g, 在图形化界面工具的情况下不写也可以)
○ 每条命令根据需要缩进 / 换行
○ 注释
★ 单行注释(#注释内容)
★ 单行注释(-- 注释内容, 注意--后的空格)
★ 多行注释(/*注释内容*/)
//2019/01/29 1点更新=========================>>>>
查询
● 基础查询SELECT
USE <数据库名> //习惯指定具体操作的数据库
○ 基本语法: select <查询列表> from <表名>
○ 特点
★ 查询列表可以是表中的字段 / 常量值 / 表达式 / 函数...
★ 查询的结果是一个虚拟的表格
○ 查询表中的单个字段: SELECT last_name FROM employees;
○ 查询表中的多个字段: SELECT last_name,salary,email FROM employees;
○ 查询表中的所有字段: SELECT * FROM employees;
○ 查询具体表的行数: SELECT COUNT(*) FROM employees;
○ 查询指定表的索引: SHOW INDEX FROM stuinfo;
○ 起别名
★ SELECT last_name AS 姓, first_name AS 名 FROM employees;
★ SELECT last_name 姓, first_name 名 FROM employees;
★ 特殊情况(查询): 当所取的别名是特殊字符时应该使用双引号(Mysql官方建议使用双引号)引起来
○ 去重(当查询结果出现多次重复时使用, 如查询某个表的员工所对应的部门编号, 此时部门编号应该会有大量相同的结果)
★ SELECT DISTINCT department_id FROM employees;
○ +号的作用(查询员工的名和姓连接成一个字段, 并显示为姓名)
★ +号在Mysql中只有运算符的作用
★ 当+号左右两边都是数值类型的时候, 则执行相加运算
★ 当只有一方是数值类型, 另一方是字符型时, Mysql会尝试将字符型也转成数值类型, 如果转换成功则执行相加运算
★ 如果转换失败, 则将字符型的数值转成0, 再执行相加运算
★ 如果其中一方为null, 则结果为null
★ 如果要执行连接操作, 则需要使用到concat函数: SELECT CONCAT(last_name, first_name) AS result FROM employees;
○ 引入ifnull函数 //判断某字段或表达式是否为null, 是则返回指定值, 否则返回原值
★ SELECT CONCAT(last_name, ',', first_name, ',', IFNULL(commission_pct,0)) AS result FROM employees
○ 引入isnull函数 //判断某字段或表达式是否为null, 是则返回1, 否则返回0
★ SELECT ISNULL(commission_pct), commission_pct FROM employees;
● 进阶查询SELECT
○ 基本语法: select <查询列表> from <表名> where <筛选条件>
○ 分类:
★ 按条件表达式筛选
* 条件运算符: > < = != 或者 <>(既小于又大于就是不等于) >= <=
* 示例: SELECT * FROM employees WHERE salary > 12000;
* 示例: SELECT last_name,department_id FROM employees WHERE department_id <> 90;
★ 按逻辑表达式筛选
* 逻辑运算符: and or not //作用就是用于连接条件表达式
* 示例: SELECT last_name 姓, salary 工资, commission_pct 奖金率 FROM employees WHERE salary AND 10000 && salary AND 20000;
* 示例: SELECT * FROM employees WHERE department_id < 90 OR department_id > 110 OR salary > 15000;
★ 模糊查询
* like //like的模糊查询不局限于字符型, 也支持数值型
▶ SELECT * FROM employees WHERE last_name LIKE '%a%'; //"%"表示任意多个字符, 包含0个字符
▶ SELECT last_name, salary FROM employees WHERE last_name LIKE '__e_a%'; //"_"表示任意单个字符
▶ SELECT last_name FROM employees WHERE last_name LIKE '_\_%'; //"\"表示转义字符
▶ SELECT last_name FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$'; //指定"$"符为转义字符, 可以指定任意字符为转义字符(Mysql官方推荐)
* between and(包含临界值)
▶ SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;
▶ ps: and左右两边的值有大小顺序(左边小, 右边大)
* in
▶ 判断某字段的值是否属于指定in列表中的某一项(只要满足一项即可)
▶ 相比or提高简洁度
▶ in列表中的值的类型必须一致或兼容
▶ in列表不支持通配符
▶ SELECT last_name, job_id FROM employees WHERE job_id IN ('IT_PORT', 'AD_VP', 'AD_PRES');
* is null(判断是否为空)
▶ SELECT * FROM employees WHERE commission_pct is NULL;
▶ SELECT * FROM employees WHERE commission_pct is NOT NULL;
* 安全等于<=> (也可用来判断是否为空)
▶ SELECT * FROM employees WHERE commission_pct <=> NULL;
//2019/01/29 13点更新=========================>>>>
排序查询
基本查询: SELECT * FROM employees ORDER BY salary DESC; //降序
SELECT * FROM employees ORDER BY salary ASC; //升序(asc可省略)
筛选条件: SELECT * FROM employees WHERE department_id >=90 ORDER BY hiredate ASC;
表达式: SELECT *, salary*12*(1+commission_pct) 年薪 FROM employees ORDER BY salary*12*(1+commission_pct) DESC; //按年薪倒序排序
SELECT *, salary*12*(1+commission_pct) 年薪 FROM employees ORDER BY 年薪 ASC; //按别名正序排序
函数: SELECT LENGTH(last_name) 字节长度, last_name, salary FROM employees ORDER BY LENGTH(last_name) DESC; //按照字节长度排序
多个排序条件: SELECT * FROM employees ORDER BY salary ASC, employee_id DESC;
//2019/01/30 1点更新=========================>>>>
常见函数
● 概念: 类似于Java的方法, 将一组逻辑语句封装在方法体中, 对外暴露方法名
● 优点
○ 隐藏细节
○ 提高代码重用性
● 调用
SELECT <函数名(实参列表)> FROM <表名> //当函数中的实参列表需要用到表中的字段时, 才需要
● 分类
☞ 单行函数
○ 字符函数
★ length函数:
* SELECT LENGTH('xxxx'); //结果为4
* SELECT LENGTH('张三'); //结果为6, 在utf8编码下一个汉字占3个字节
★ concat函数 / upper函数(大写) / lower函数(小写)
* SELECT CONCAT(last_name,'_',first_name) FROM employees; //拼接字符串, 当参数来自某张表时则需要写上FROM <表名>
* SELECT CONCAT(UPPER(last_name),LOWER(first_name)) FROM employees; //将姓转成大写, 名转成小写并拼接
* SELECT SUBSTR('宇宙浩瀚, 银河灿烂', 7); //结果截取了"银河灿烂"四个字符. ps: 所有的sql语句的索引都是从1开始的, 此sql截取从索引7往后的字符
* SELECT SUBSTR('银河璀璨, 心胸如它一般广阔', 1, 4); //截取从索引1开始往后数4个长度的字符, 为银河璀璨
* SELECT SUBSTRING('浩瀚的星空, 渺小的地球', 1, 5); //SUBSTRING的用法同SUBSTR
* SELECT INSTR('this is a test sql','test sql'); //返回子串在父串中第一次出现的索引值, 如果没有, 则返回0
* SELECT TRIM('abc' FROM 'abcabcab张三abc李四abc王五abcbc') AS output; //去掉"abcabcab张三abc李四abc王五abcbc"中的"abc", 结果: ab张三abc李四abc王五abcbc, 前面剩下ab不符合, 后面abcbc无法识别出来, 中间不去除
* SELECT LPAD('地球/月球/火星/',10,'*-'); //意为如果不满10个字符, 则用"*-"在左侧补齐, 结果: "*地球/月球/火星/"
* SELECT RPAD('地球/月球/火星/',10,'*-'); //意为如果不满10个字符, 则用"*-"在右侧补齐, 结果: "地球/月球/火星/*"
* SELECT REPLACE('地球月球月球月球月球/地球地球月球/火星/', '月球', '天王星') output; //将"月球"替换成"天王星"
○ 数学函数
★ round //四舍五入
* SELECT ROUND(1.49); //结果为1
* SELECT ROUND(-1.59); //结果为-2, 可以看成去掉负号后的四舍五入再加回负号
* SELECT ROUND(1.418,2) //保留2位小数, 结果: 1.42
★ ceil //向上取整, 返回大于等于指定参数的最小整数
* SELECT CEIL(1.001); //结果: 2
* SELECT CEIL(-1.001); //结果: -1
★ floor //向下取整, 返回小于等于指定参数的最大整数
* SELECT FLOOR(-1.999); //返回-2
★ truncate //从某处截断某个字符串
* SELECT TRUNCATE(-11.7999,2); //返回-11.79
★ mod //取模值
* SELECT MOD(-10,-3); //公式: mod(a,b)=a-a/b*b;
//2019/01/30 10点更新=========================>>>>
○ 日期函数
★ now //返回当前系统日期(包含时间)
* SELECT NOW(); //结果: 2019-01-30 09:00:08
★ curdate //返回当前系统日期(不包含时间)
* SELECT CURDATE(); //2019-01-30
★ curtime //返回当前的时间(不包含日期)
* SELECT CURTIME(); //09:03:25
★ datediff //返回两个日期之间的天数(前一个参数减去后一个参数)
* SELECT DATEDIFF(NOW(),'2000-7-15'); //可以获知自己活了多少天
★ //获取指定的部分(年 / 月 / 日 / 时 / 分 / 秒)
* SELECT YEAR(NOW()) 年, MONTH(NOW()) 月, DAY(NOW()) 日, HOUR(NOW()) 时, MINUTE(NOW()) 分, SECOND(NOW()) 秒; //可以自由组装
* SELECT MONTH('2019-1-1'); //也可以自定义时间
* SELECT YEAR(hiredate) 年 FROM employees; //查询指定表的入职日期的年份
* SELECT MONTHNAME(NOW()) 月名; //英文的月名January
* SELECT DAYNAME(NOW()) 星期; //英文的星期Wednesday
★★ str_to_date //将字符通过指定格式转成日期(重要)
* SELECT * FROM employees WHERE hiredate = STR_TO_DATE('01-30 2019','%m-%d %Y'); //按照指定格式去解析字符串里面的内容, 并按照该日期查询
★★ date_format //将日期格式化(重要)
* SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日 %H时%i分%s秒') AS 日期; //结果: 2019年01月30日 09时55分48秒
* SELECT last_name 姓名, DATE_FORMAT(hiredate, '%m月%d日 %y年') 入职日期 FROM employees WHERE commission_pct IS NOT NULL; //查询指定年月日格式及满足奖金率条件的员工入职信息, 结果: 12月23日 02年
★ 附:
* %Y: 四位数年份
* %y: 两位数年份
* %m: 补0月份
* %c: 不补0月份
* %d: 日
* %H: 时(24小时制)
* %h: 时(12小时制)
* %i: 分
* %s: 秒
○ 几个系统函数
★ SELECT VERSION(); //查询Mysql的版本信息
★ SELECT DATABASE(); //查询当前所在数据库
★ SELECT USER(); //查询当前的用户
//2019/01/30 13点更新=========================>>>>
○ 流程控制函数
★ if
* SELECT last_name, commission_pct, IF(commission_pct IS NULL, '呵呵', '嘻嘻') 备注 FROM employees; //相当于if...else..的效果
★ case...when...end
▶ case <要判断的变量或表达式> when <常量1> then <要显示的值1或语句1>; //当此处不是语句时不能放分号, 否则认为 是结束标志(下同)
when <常量1> then <要显示的值1或语句1>; ... else 要显示的值n或语句n; end
* SELECT salary, department_id, CASE department_id WHEN 30 THEN salary*1.1 WHEN 40 THEN salary*1.2 WHEN 50 THEN salary*1.3 ELSE salary END AS 总工资 FROM employees; //不同部门的工资+奖金不同
▶ case when <条件1> then <要显示的值1或语句1> when <条件2> then <要显示的值2或语句2> ... else <要显示的值n或语句n> end
* SELECT salary, CASE WHEN salary>20000 THEN 'A' WHEN salary>15000 THEN 'B' WHEN salary>10000 THEN 'C' ELSE 'D' END AS 工资级别 FROM employees; //查询工资级别
☞ 分组函数
○ sum / avg / min / max / count
★ SELECT SUM(salary) from employees; //处理数值类型, 并且忽略null值(值为null的字段不参与运算)
★ SELECT AVG(salary) FROM employees; //处理数值类型, 并且忽略null(值为null的字段不参与运算)
★ SELECT MIN(salary) FROM employees; //处理可排序的字段, 并且忽略null值(值为null的字段不参与运算)
★ SELECT MAX(salary) FROM employees; //处理可排序的字段, 并且忽略null值(值为null的字段不参与运算)
★ SELECT COUNT(salary) FROM employees; //计算非空字段的个数
★ SELECT SUM(salary) 和, ROUND(AVG(salary),2) 平均, MIN(salary) 最低, MAX(salary) 最高, COUNT(salary) 个数 from employees;
○ 配合distinct实现去重运算
★ SELECT COUNT(DISTINCT salary) 去重后个数, COUNT(salary) FROM employees;
○ 使用频率最多的count函数
★ SELECT COUNT(*) FROM employees; //查询表的总行数(只要有一行对应的所有列不全为空, 则统计进去)
★ SELECT COUNT(1) FROM employees; //效果跟count(*)一样, 相当于统计一列每行都是1的个数, 所以也是统计总行数
○ 和分组函数一同查询的字段必须是group by后面的字段 //group by分组后续会写到
//2019/01/31 10点更新=========================>>>>
○ 分组查询group by
ps: ①查询列表必须是分组函数或者group by后出现的字段
②分组查询中的筛选条件分为两类==> where(用于分组前) 和 having(用于分组后, 尽量不使用) //where不用赘述, having在下边会写到
▶ 语法: select <分组函数>, 列(该列的字段必须在group by后面有出现) from <表名> where <筛选条件> group by <分组列表>
★ SELECT MAX(salary) 最高工资, job_id 工种Id FROM employees GROUP BY job_id; //查询不同工种的最高工资
★ SELECT COUNT(*), location_id FROM departments GROUP BY location_id; //查询不同位置的部门个数
★ SELECT AVG(salary), department_id FROM employees WHERE email LIKE '%a%' GROUP BY department_id; //分组前的筛选
★ SELECT MAX(salary), manager_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY manager_id;
▶ having //放在group by之后(分组函数做条件需要放在having后, 尽量不使用having)
ps: 分组函数做条件必须放在group by的子句中
★ SELECT COUNT(*), department_id from employees GROUP BY department_id HAVING COUNT(*) > 2; //分组后的筛选
★ SELECT MAX(salary), job_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY job_id HAVING MAX(salary) > 12000;
★ SELECT MIN(salary), manager_id FROM employees WHERE manager_id > 102 GROUP BY manager_id HAVING MIN(salary) > 5000;
★ SELECT COUNT(*), LENGTH(last_name) FROM employees GROUP BY LENGTH(last_name) HAVING COUNT(*) > 5;
★ SELECT AVG(salary) 工资, department_id 部门Id, job_id 工种 FROM employees GROUP BY 部门Id, 工种; //支持使用别名, ps: mysql的where不支持别名, group by 和 having支持别名; 而oracle中都不支持
★ SELECT AVG(salary) 工资, department_id 部门Id, job_id 工种 FROM employees WHERE department_id IS NOT NULL GROUP BY 部门Id, 工种 HAVING AVG(salary) > 10000 ORDER BY AVG(salary) DESC; //分组 / 排序 / 条件查询
//2019/01/31 13点更新=========================>>>>
连接查询(又称多表查询)
○ 示例
★ SELECT `name`, boyName FROM boys, beauty WHERE boys.id = beauty.boyfriend_id; //如果不给出限定条件则会出现笛卡尔集的效果, 这是不被允许的
○ 按年代分类
★ sql92标准: 仅支持内连接
★ sql99标准(推荐): 支持内连接+外连接(左外连接+右外连接)+交叉连接
○ 按功能分类
★ 内连接
* 等值连接
* 非等值连接
* 自连接
★ 外连接
* 左外连接
* 右外连接
* 全外连接
★ 交叉连接
○ sql92标准
▼ 等值连接
※ SELECT last_name 员工名称, department_name 部门名称 FROM employees, departments WHERE employees.department_id = departments.department_id; //将员工和对应的部门查询出来
※ SELECT e.last_name 员工名称, j.job_id 工种号, j.job_title 工种名 FROM employees e, jobs j WHERE e.job_id = j.job_id; //如果已经给表起了别名, 则查询的字段不能使用表名去限定
※ SELECT e.last_name 员工姓名, d.department_name 部门名称, e.commission_pct 奖金额 FROM employees e, departments d WHERE e.department_id = d.department_id AND e.commission_pct IS NOT NULL; //筛选查询
※ SELECT d.department_name 部门名称, l.city 所在城市 FROM departments d, locations l WHERE d.location_id = l.location_id AND l.city LIKE '_o%'; //筛选查询
※ SELECT count(*) 数量, l.city 城市 FROM departments d, locations l WHERE d.location_id = l.location_id GROUP BY l.city; //分组查询
※ SELECT d.department_name 员工姓名, d.manager_id 领导编号, MIN(salary) 最低工资 FROM departments d, employees e WHERE e.department_id = d.department_id AND commission_pct IS NOT NULL GROUP BY d.department_name, d.manager_id;
※ SELECT COUNT(*) 员工人数, j.job_title 工种名称 FROM jobs j, employees e WHERE j.job_id = e.job_id GROUP BY j.job_title ORDER BY COUNT(*) DESC;
※ SELECT e.last_name 员工姓名, d.department_name 部门名称, l.city 所在城市 FROM employees e, departments d, locations l WHERE e.department_id = d.department_id AND d.location_id = l.location_id AND l.city LIKE 's%' ORDER BY d.department_name DESC; //三表连接
sql92标准等值连接小结:
※ 多表的等值连接的结果为多表的交集部分
※ n表的连接, 至少需要(n-1)个连接条件
※ 多表的顺序没有要求
※ 一般需要为表起别名
※ 可以搭配之前的所有子句使用, 如: 排序 / 分组 / 筛选...
▼非等值连接
※ SELECT e.salary 员工工资, jg.grade_level 工资等级 FROM employees e, job_grades jg WHERE e.salary BETWEEN jg.lowest_sal AND jg.highest_sal AND jg.grade_level = 'A';
▼自连接(类似于等值连接, 区别在于是自己连接自己)
※ SELECT e.employee_id 员工Id, e.last_name 员工姓名, m.employee_id 领导Id, m.last_name 领导姓名 FROM employees e, employees m WHERE e.manager_id = m.employee_id; //自连接查询
//2019/02/01 16点更新=========================>>>>
○ sql99标准
▼ 语法
※ select <查询列表> from <表1> <别名> <连接类型> join <表2> <别名>
※ 连接类型
★ 内连接: inner
★ 外连接: 左外(left [outer])、右外(right [outer])、全外(full [outer])
★ 交叉连接: cross
▼ 等值连接
※ SELECT e.last_name 员工姓名, d.department_name 部门名称 from employees e INNER JOIN departments d ON e.department_id = d.department_id; //内连接查询
※ SELECT e.last_name 员工姓名, job_title 工种名称 FROM employees e JOIN jobs j ON e.job_id = j.job_id WHERE e.last_name LIKE '%e%'; //inner可省略, 跟sql92标准的等值连接效果一样
※ SELECT l.city 城市名称, count(*) 部门个数 FROM locations l INNER JOIN departments d ON l.location_id = d.location_id GROUP BY city HAVING count(*) > 3; //连接条件放在on后面, 跟where的筛选条件分离, 提高阅读性
※ SELECT d.department_name 部门名称, COUNT(*) 部门个数 FROM employees e INNER JOIN departments d ON e.department_id = d.department_id GROUP BY department_name HAVING COUNT(*) > 3 ORDER BY COUNT(*) DESC; //分组 / 筛选 / 排序联合查询
※ SELECT e.last_name 员工名称, d.department_name 部门名称, j.job_title 工种名称 FROM employees e INNER JOIN departments d ON e.department_id = d.department_id INNER JOIN jobs j ON e.job_id = j.job_id ORDER BY d.department_name DESC; //3表连接
▼ 非等值连接
※ SELECT e.salary 员工工资, jg.grade_level 工资级别 FROM employees e INNER JOIN job_grades jg ON e.salary BETWEEN jg.lowest_sal AND jg.highest_sal;
※ SELECT COUNT(*) 个数, jg.grade_level 工资级别 FROM employees e INNER JOIN job_grades jg ON e.salary BETWEEN jg.lowest_sal AND jg.highest_sal GROUP BY jg.grade_level HAVING COUNT(*) > 20 ORDER BY grade_level DESC;
▼ 自连接
※ SELECT e1.last_name 员工名称, e2.last_name 领导名称 FROM employees e1 INNER JOIN employees e2 ON e1.manager_id = e2.employee_id WHERE e1.last_name LIKE '%k%'; //查询员工跟对应的领导名称
//2019/02/02 0点更新=========================>>>>
▼ 外连接 (左右)
※ 前面的内连接查询的都是两张或者多张表的交集部分, 而外连接就是要查询差集的部分
※ 外连接是有分主表跟从表的概念的, 其查询结果为主表中的所有记录, 如果从表中有和主表相匹配的则显示, 没有相匹配的则显示null, 所以, 总而言之, 外连接的查询结果=内连接的查询结果+主表中有而从表中没有的记录(没有的列用null显示)
※ 区分主表跟从表
★ 左外连接: left join的左边是主表
★ 右外连接: right join的右边是主表
※ 示例
★ SELECT b.`name` FROM beauty b LEFT OUTER JOIN boys bo ON b.boyfriend_id = bo.id WHERE bo.id IS NULL; //左外连接, outer可省略
★ SELECT b.`name` FROM boys bo RIGHT OUTER JOIN beauty b ON bo.id = b.boyfriend_id WHERE bo.id IS NULL; //右外连接
★ SELECT d.*, e.employee_id FROM departments d LEFT OUTER JOIN employees e on e.department_id = d.department_id WHERE e.employee_id IS NULL; //where条件其实就是去掉内连接的部分
※ 全外连接mysql不支持; 交叉连接的效果相当于笛卡尔集, 也没有意义, 不做展示.
※ sql99支持的语法较sql92的多, 同时实现连接条件和筛选条件分离, 可读性较高, 建议使用.
//2019/02/03 0点更新=========================>>>>
子查询(难度较高)
释义: 出现在select语句或其他语句(增删改等语句)中的查询语句, 称之为子查询或内查询
与之对应的外部的查询语句称之为主查询语句
分类:
※ 出现的位置
★ select后面
* 只能支持结果集为一行一列的标量子查询
★ from后面
* 支持表子查询(一定要带上别名, 否则相当于找不到该表, 会报错)
★ where或having后面(经常使用)
* 支持标量子查询 / 列子查询 / 行子查询(较少使用)
★ exists后面(又称相关子查询)
* 支持所有子查询, exists的子查询结果不重要, 它只判断是true还是false
※ 结果集的行列数
★ 标量子查询(结果集为一列一行)
★ 列子查询(结果集为一列多行)
★ 行子查询(结果集为多行多列)
★ 表子查询(结果集为以上3种任一种即可, 一般为多行多列)
※ 子查询的特点
★ 子查询放在小括号内
★ 子查询放在条件的右侧
★ 标量子查询搭配条件运算符(< > <> <= >= =)一起使用
★ 列子查询搭配(in and/some all)一起使用
★ 子查询的优先级大于主查询, 因为主查询需要用到子查询的结果
※ 标量子查询的使用
★ SELECT e.salary 工资 FROM employees e WHERE e.last_name='Abel'; //子查询Abel的工资
★ SELECT e.last_name 员工姓名, e.salary 员工工资 FROM employees e WHERE e.salary > (SELECT e.salary 工资 FROM employees e WHERE e.last_name='Abel'); //在where的筛选条件里面使用子查询
★ SELECT e.last_name 员工姓名, e.job_id, e.salary 工资 FROM employees e WHERE e.job_id = (
SELECT e.job_id FROM employees e WHERE e.employee_id = 141
) AND e.salary > (
SELECT e.salary FROM employees e WHERE e.employee_id = 143
); //使用多个子查询
★ SELECT MIN(e.salary), e.department_id FROM employees e GROUP BY e.department_id HAVING MIN(e.salary) > (SELECT MIN(e.salary) FROM employees e WHERE e.department_id = 50) AND e.department_id IS NOT NULL; //分组函数的使用
※ 列子查询的使用
★ SELECT e.last_name 员工姓名 FROM employees e WHERE e.department_id IN(SELECT DISTINCT d.department_id FROM departments d WHERE d.location_id IN(1400, 1700));
▶ SELECT e.employee_id, e.last_name, e.job_id, e.salary FROM employees e WHERE e.salary < ANY(
SELECT DISTINCT e.salary FROM employees e WHERE e.job_id = 'it_prog'
) AND e.job_id <> 'it_prog'; //Any函数, 意为比括号内的任意一个值都小
//等价于
▶ SELECT e.last_name, e.employee_id, e.job_id, e.salary FROM employees e WHERE e.salary < (
SELECT MAX(e.salary) FROM employees e WHERE e.job_id = 'it_prog'
) AND e.job_id != 'it_prog'; //直接使用成max函数
▷ SELECT e.employee_id, e.last_name, e.job_id, e.salary FROM employees e WHERE e.salary < ALL(
SELECT DISTINCT e.salary FROM employees e WHERE e.job_id = 'it_prog'
) AND e.job_id <> 'it_prog'; //all函数, 意为比括号内的最小值还小
//等价于
▷ SELECT e.employee_id, e.last_name, e.job_id, e.salary FROM employees e WHERE e.salary < (
SELECT MIN(e.salary) FROM employees e WHERE e.job_id = 'it_prog'
) AND e.job_id !='it_prog'; //直接使用min函数
★ not in 等价于 <> all / in 等价于 =any
//2019/02/03 12点更新=========================>>>>
※ 行子查询的使用(用的不多)
★ SELECT * FROM employees e WHERE e.employee_id = (
SELECT MIN(e.employee_id) FROM employees e
) AND e.salary = (
SELECT MAX(e.salary) FROM employees e
); //两个或多个=号的查询可以使用行子查询, 如下
★ SELECT * FROM employees e WHERE (e.employee_id, e.salary) = (
SELECT MIN(e.employee_id), MAX(e.salary) FROM employees e
); //这种行子查询方式有很大的局限性, 所以使用不多
※ select后面的子查询(要求是子查询的结果必须是一行一列)
★ SELECT d.*, (
SELECT COUNT(*) FROM employees e WHERE e.department_id = d.department_id
) 该部门员工个数 FROM departments d;
※ from后面的子查询(该子查询充当一个表结果)
★ SELECT avgs.*, jg.grade_level 工资等级 FROM (
SELECT AVG(e.salary) avg, e.department_id FROM employees e GROUP BY e.department_id
) avgs INNER JOIN job_grades jg ON avgs.avg BETWEEN jg.lowest_sal AND jg.highest_sal; //from后面的子查询充当一张表, 且必须起别名, 否则无法找到该表结果
※ exists后面的子查询(又称相关子查询)
☞ 释义: exists就是判断其对应的子查询的结果是否有值, 有则返回1, 无则返回0
★ SELECT EXISTS(SELECT * FROM employees); //返回1
★ SELECT EXISTS(SELECT * FROM employees e WHERE e.employee_id = 3000); //返回0
▶ SELECT d.department_name 部门名称 FROM departments d WHERE EXISTS(
SELECT * FROM employees e WHERE e.department_id = d.department_id
);
//等价于
▶ SELECT d.department_name 部门名称 FROM departments d WHERE d.department_id IN (
SELECT e.department_id FROM employees e
);
▷ SELECT bo.* FROM boys bo WHERE NOT EXISTS(
SELECT * FROM beauty b WHERE b.boyfriend_id = bo.id
);
//等价于
▷ SELECT bo.* FROM boys bo WHERE bo.id NOT IN (
SELECT b.boyfriend_id FROM beauty b
);
分页查询(重点)
☞ 释义: 当要显示的数据量过大, 一页无法完全显示, 则需要分页提交sql请求
※ 语法: 以上所提到的查询语法 + 最后的limit语句
※ limit: 第一个参数(offset)是起始索引(索引从0开始), 第二个参数(pageSize)是每页要展示的条目个数
★ 最简单的示例: SELECT * FROM employees LIMIT 0,5; //查询前5条
★ SELECT * FROM employees LIMIT 5; //可省略初始索引(默认是索引为0)
★ SELECT * FROM employees e WHERE e.commission_pct IS NOT NULL ORDER BY e.salary DESC LIMIT 10; //limit放在最后
☆ limit不管在语法还是在sql的执行顺序上都是排在最后
☆ limit的起始页码的公式: (页数-1)*每页条目个数, 也就是offset = ([当前页数] - 1 )*[pageSize]
//2019/02/07 12点更新=========================>>>>
联合查询
● 释义: 将多条查询语句的结果合并成一个结果
● 语法: 查询语句① union 查询语句② union ...
● 示例:
SELECT * FROM employees e WHERE e.email LIKE '%a%' -- 查询语句①
UNION -- 联合①和②
SELECT * FROM employees e WHERE e.department_id > 90; -- 查询语句②
● 应用场景: 要查询的结果来自于多个表且每个表之间没有连接关系(外键), 但要得到的查询的信息是一致的
SELECT id, cname, csex FROM t_ca WHERE csex = '男' -- 表①
UNION -- 联合表①和表②的查询语句, 并且两张表没有外键关联
SELECT t_id, tName, tGender FROM t_ua WHERE tGender = 'male'; -- 表②
● 特点
○ 要求所有的联合查询的查询语句的列数是一致的 //不一致则报错
○ 要求多条查询语句的每一列的类型和顺序最好是一致的 //不报错, 但是顺序比较混乱不好查看
○ 使用union关键字会自动去掉重复项
○ 使用union all关键字不会自动去掉重复项
//2019/02/08 17点更新=========================>>>>
数据操作语言(DML)
☞ 数据的插入(新增)
● 语法
① insert into 表名(列名1,列名2,...) values(值1,值2,...);
② insert into 表名 set 列名=值, 列名=值 ...
○ 示例: INSERT INTO beauty(id, `name`, sex, borndate, phone, photo, boyfriend_id) VALUES(13, '张三', '女', '1995-4-4', '13888888888', NULL, 2); //插入的值的类型与列的类型一致, 如果可以允许为空, 则可以插入null
○ INSERT INTO beauty VALUES(16,'张飞','男','2019/1/1 12:21:12','119',NULL,NULL); //可以不写列名, 按照表的默认列, 此时可以为空的必须写null
○ INSERT INTO beauty SET id = 17, `name` = '关羽', phone = '7788'; //这种插入数据的方法比较方便, 可以为空的则不写即可
● 方式①和方式②插入数据的特点
○ INSERT INTO beauty VALUES(18,'张飞','男','2019/1/1 12:21:12','119',NULL,NULL), (19,'张飞','男','2019/1/1 12:21:12','119',NULL,NULL); //方式①可以支持一条sql语句中插入多条数据, 方式②不支持
○ INSERT INTO beauty(id,`name`,phone) SELECT 20, '王五', '9988'; //方式①支持子查询, 方式②不支持 ==> SELECT 20, '王五', '9988' 可以换成查询语句对应插入数据
//2019/02/09 02点更新=========================>>>>
☞ 修改单表的数据(*)
● 语法
▶ update 表名 set 列名=新值,列名=新值,... where <筛选条件>;
○ UPDATE beauty SET phone = '110119120' WHERE `name` LIKE '张%'; //简单修改
☞ 修改多表的数据
● 语法
▶ update 表1 别名, 表2 别名 set 列名=新值,... where <连接条件> and <筛选条件>; //sql92语法
▶ update 表1 别名 inner | left | right join 表2 别名 on <连接条件> set 列名=新值,... where <筛选条件>; //sql99语法
○ UPDATE boys bo INNER JOIN beauty b ON bo.id = b.boyfriend_id SET b.phone = '192168' WHERE bo.boyName = '张无忌'; //简单的多表修改
○ UPDATE boys bo RIGHT JOIN beauty b ON bo.id = b.boyfriend_id SET b.boyfriend_id = 2 WHERE bo.id IS NULL; //使用外连接的方式修改多表中的数据
☞ 删除单表中的数据(*)
● 语法
▶ delete from 表名 where <筛选条件> //单表的删除
▶ truncate table 表名; //删除整个表的数据, 不能使用where追加筛选条件
○ DELETE FROM beauty WHERE phone LIKE '%'; //简单单表删除数据, delete关键字后面不能有*, 也不能有表的别名
○ TRUNCATE TABLE beauty; //将指定表中的数据全部删除, 无法级联, 也无法追加筛选条件
● delete和truncate的区别
○ delete可以追加where筛选条件, truncate不能
○ delete如果删除全表数据后, 再插入数据时自增是从断点时继续开始; 而truncate则是从1开始
○ delete删除有返回值, 而truncate删除无返回值
○ delete删除是可以回滚的, 而truncate无法回滚
○ delete后面能追加limit <删除条数> //会从上往下删除指定条数
○ truncate的效率较delete高
☞ 删除多表中的数据
● 语法
▶ delete 表1的别名, 表2的别名 from 表1 别名, 表2 别名 where <连接条件> and <筛选条件>; //sql92语法
▶ delete 表1的别名, 表2的别名 from 表1 别名 inner | left | right join 表2 别名 on <连接条件> where <筛选条件>; //sql99语法
○ DELETE b,bo FROM beauty b INNER JOIN boys bo ON b.boyfriend_id = bo.id WHERE bo.boyName = '张无忌'; //仅仅删除两张表中与筛选条件符合的数据, 而非删除两张表中全部的数据
//2019/02/11 14点更新=========================>>>>
DDL(数据定义语言)
☞ 库的管理
● 创建(create) / 修改(alter) / 删除(drop)
○ 创建库
▶ create database <数据库名称> //创建一个新的数据库
示例: CREATE DATABASE books [CHARACTER SET utf8]; //普通建库
CREATE DATABASE IF NOT EXISTS books [CHARACTER SET utf8]; //容错性建库
○ 修改库(不建议中途修改数据库)
示例: ALTER DATABASE books [CHARACTER SET utf8]; //将数据库的字符集修改为utf8
○ 删除库
示例: DROP DATABASE books; //删除数据库
DROP DATABASE IF EXISTS books; //容错性删除库
☞ 表的管理
● 创建(create) / 修改(alter) / 删除(drop)
○ 创建表
▶ create table <表名>(
列名 列的类型[长度 约束],
...
列名 列的类型[长度 约束]
)
示例:
DROP TABLE IF EXISTS book;
CREATE TABLE book(
id INT, #编号
bName VARCHAR(20), #书名
price DOUBLE, #价格
authorId INT, #作者编号
publishDate DATETIME #出版日期
);
DESC book; //显示表结构信息
DROP TABLE IF EXISTS author;
CREATE TABLE author(
id INT,
au_name VARCHAR(20),
nation varchar(10)
);
DESC author;
○ 修改表
① 修改列名
示例: ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME; //需要加上类型, 相当于可以同时修改类型
② 修改列的类型或约束
示例: ALTER TABLE book MODIFY COLUMN publishDate TIMESTAMP; //关键字为MODIFY
③ 添加新列
示例: ALTER TABLE author ADD COLUMN annual DOUBLE; //默认将列添加到最后
ALTER TABLE author ADD COLUMN annual DOUBLE FIRST; //将列添加到最前
ALTER TABLE author ADD COLUMN annual DOUBLE AFTER <指定列名>; //将列添加到指定列之后
④ 删除列
示例: ALTER TABLE author DROP COLUMN annual; //关键字为DROP
⑤ 修改表名
示例: ALTER TABLE author RENAME TO book_author; //关键字为RENAME
○ 删除表
示例: DROP TABLE IF EXISTS book_author; //容错性处理
○ 复制表
仅复制表结构
示例: CREATE TABLE copy LIKE author; //仅复制表author的表结构到表copy中
复制表结构和数据
示例: CREATE TABLE copy2 SELECT * FROM author; //复制表author的表结构和数据到表copy2中
CREATE TABLE copy3 SELECT id, au_name FROM author WHERE nation = '中国'; //仅表author中满足where条件的表结构和数据到表copy3中, 此时仅复制select的对应的列的结构和数据
CREATE TABLE copy4 SELECT id, au_name FROM author WHERE 0; //仅复制表author中满足where条件的表结构到表copy4中
//2019/02/12 0点更新=========================>>>>
☞ 数据类型
● 整型(整数)
○ tinyint: 占用1个字节
○ smallint: 占用2个字节
○ mediumint: 占用3个字节
○ int / integer: 占用4个字节
○ bigint: 占用8个字节
★ 默认的情况下, 整型都是有符号的, 如果要指定成无符号类型, 则要加上UNSIGNED关键字
CREATE TABLE tab_int(
id INT,
id2 INT UNSIGNED # 指定关键字UNSIGNED
);
★ 可以追加ZEROFILL关键字使其当不够位数时前面补零, 但就会强制转化成无符号数
CREATE TABLE tab_int(
id INT(8) ZEROFILL, # 指定关键字UNSIGNED, 指定长度为8, 不够位数前面自动补零
id2 INT ZEROFILL # 指定关键字ZEROFILL, 没有指定位数, 但有默认长度, 不够位数前面自动补零
);
● 小数
○ 浮点型
★ float(M,D): 占用4个字节 //M和D分别是指总位数(整数位数+小数位数)和小数点后的位数(可省略)
★ double(M,D): 占用8个字节
○ 定点型
★ DEC(M,D) / Decimal(M,D): 占用字节为M+2, 取值范围比double更加精确, 如果需要保存较为精确的数据(货币运算), 则推荐使用定点型decimal
CREATE TABLE tab_float(
f1 FLOAT(5,2),
f2 DOUBLE(5,2),
f3 DECIMAL(5,2)
);
INSERT INTO tab_float VALUES(123.45,123.45,123.45); //只能插入两位小数的数据
CREATE TABLE tab_float(
f1 FLOAT,
f2 DOUBLE, //当省略(M,D)时, float和double是根据插入值的精度来决定当前字段的精度
f3 DECIMAL //当省略(M,D)时, decimal默认精度为(10,0)
);
● 字符型
○ char(M) / varchar(M) //短文本, M表示该字段最多能有M个字符, 一个字母是一个字符, 一个汉字也是一个字符
★ char: 固定长度的字符, 写法为char(M), 最大长度不能超过M, 可以省略M, 默认长度为1
★ varchar: 可变长度的字符, 写法varchar(M), 最大长度不能超过M, 不可以省略M
★ 可变与不可变的区别在于, 当你插入的数据是2个字符时, 此时如果用char(10), 数据库依然会开辟10个字符的空间; 而如果使用varchar(10), 数据库则开辟2个字符的空间; char的性能比varchar的性能好; char可以省略(M)而varchar不能省略(M)
○ text / blob //长文本
● 日期型
○ date: 只有日期没有时间
○ time: 只有时间没有日期
○ year: 只有年
○ datetime: 有时间也有日期, 占用8字节, 范围1000-9999, 不受时区的影响(展示当前系统时间)
○ timestamp: 有时间也有日期, 占用4字节, 范围1970-2038, 受时区的影响(展示数据库所在时区的时间, 更能反映当前时区的真实时间, 推荐使用)
CREATE TABLE tab_date(
t1 DATETIME,
t2 TIMESTAMP
);
INSERT INTO tab_date VALUES(
NOW(),NOW()
);
//2019/02/14 23点更新=========================>>>>
☞ 约束
● 六大约束
○ NOT NULL: 非空约束
○ DEFAULT: 默认值约束
○ PRIMARY KEY: 主键约束(非空)
○ UNIQUE: 唯一约束(可以为空)
○ foreign key: 外键约束(在从表中添加外键关联约束, 引用主表中的值)
○ check: 检查约束(mysql不支持, 无效果)
● 约束的分类:
○ 列级约束: 六大约束语法上支持, 但外键约束没有效果
○ 表级约束: 非空约束 / 默认约束不支持
○ 添加约束:
▷ 添加列级约束
CREATE TABLE stuinfo (
id INT PRIMARY KEY, #主键约束
stuName VARCHAR(20) NOT NULL, #非空约束
seat INT UNIQUE, #唯一约束
age INT DEFAULT #默认约束
);
▷ 添加表级约束
CREATE TABLE stuinfo (
id INT,
stuname VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorId INT,
CONSTRAINT pk PRIMARY KEY(id),
CONSTRAINT uq UNIQUE(seat),
CONSTRAINT ck CHECK(gender='男' OR gender='女'), #检查约束无效
CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorId) REFERENCES major(id)
);
ps: 其中的"CONSTRAINT <别名>"可以不写, 没有指定别名时会有默认名称.
▷ 通用的写法
CREATE TABLE IF NOT EXISTS stuinfo(
id INT PRIMARY KEY,
stuname VARCHAR(20) NOT NULL,
gender CHAR(1),
age INT DEFAULT 23,
seat INT UNIQUE,
majorId INT,
CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorId) REFERENCES major(id)
);
▷ 主键约束和唯一约束的对比
★ 主键约束和唯一约束都能保证唯一性, 但唯一约束不能都为null
★ 主键约束不允许为空, 但唯一约束允许为空
★ 同一张表中至多只能有一个主键约束, 但唯一键约束可以有多个
★ 主键约束和唯一约束都支持组合, 如下
CONSTRAINT pk PRIMARY KEY(id,stuname) //组合主键约束, 此时只有当插入两条数据的这两个值的内容对应相同时才会发挥组合主键的作用; 唯一约束也是同理
▷ 外键约束需要注意的点
★ 需要在从表中设置外键关系
★ 从表的外键列的类型和主表的关联列的类型要一致或兼容
★ 主表中的关联列必须是一个key(一般是主键或唯一键)
▷ 修改表时添加约束
※ 语法
添加列级约束: alter table <表名> modify column <列名> <类型> <约束>;
添加表级约束: alter table <表名> add <约束(列名)>;
添加外键约束: alter table <表名> ADD [constraint <别名>] foreign key (<从表外键列名>) references <主表(关联列名)>;
★ ALTER TABLE stuinfo MODIFY COLUMN gender VARCHAR(20) NOT NULL; //添加列级约束, 如果要修改为空, 则写成null或者不写(默认就是null), 其他约束的修改同理
☆ ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE; //添加列级约束
☆ ALTER TABLE stuinfo ADD UNIQUE(seat); //添加表级约束
★ ALTER TABLE stuinfo ADD FOREIGN KEY(majorId) REFERENCES major(id); //添加表级约束
▷ 小结列级约束和表级约束
位置 | 支持的约束类型 | 是否允许起别名 | |
列级约束 | 每个列的后面 | 语法都支持, 但外键无效果; 不支持检查约束 | 不可以 |
表级约束 | 所有列的后面 | 不支持默认约束、非空约束和检查约束 | 语法支持, 但无效果 |
//2019/02/15 11点更新=========================>>>>
▷ 修改表时删除约束
★ ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL; //null可写可不写(默认就是null)
★ ALTER TABLE stuinfo DROP PRIMARY KEY; //直接删除主键约束, 主键一般就一个, 可以不指定名称
★ ALTER TABLE stuinfo DROP INDEX seat; //删除索引, 索引可能有多个, 需要指定名称
★ ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major; //删除指定外键, 在创建时最好自定义外键名称
▷ 标识列
★ 创建表时添加自增标识列
CREATE TABLE tab_identity(
id INT PRIMARY KEY auto_increment, # 自增标识只需要加在一个Key上, 不一定非要主键; 一张表只能有一个自增列; 只能加在类型为数值型的列上
name VARCHAR(20)
);
★ 修改表时添加自增标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT auto_increment;
★ 修改表时删除自增标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT;
//2019/02/16 11点更新=========================>>>>
事务
● 什么是事务(ACID)
★ 原子性(Atomicity): 一个事务不可再分割
★ 一致性(Consistency): 多个事务执行前后的总状态保持不变(比如转账前后双方的总金额)
★ 隔离性(Isolation): 一个事务的执行不会受到其他事务的干扰(根据数据库的隔离级别不同而有所区别)
★ 持久性(Durability): 事务一旦提交, 则永久改变数据库的数据
● 事务的分类
★ 隐式事务: 事务没有明显的开启和结束标记(比如insert / update / delete等语句), 默认的自动提交功能为on(开启中)
★ 显式事务: 事务具有明显的开启和结束的标记, 前提需要设置自动提交功能为0(已关闭) //set autocommit = 0
● 操作事务
★ 开启事务(默认在执行sql之前就会自动开启): start transaction
★ 结束事务: commit; / rollback; //提交 / 回滚事务
● 常见的并发问题(隔离级别不同导致的)
① 脏读
场景: 当A事务更新数据后在没有提交的情况下能够被B事务查询到, 此为脏读(按正常逻辑未提交的事务应该是不能被查询到的, 因为此时A事务有可能会做撤销(rollback)操作)
② 不可重复读
场景: 当A事务更新数据后在没有提交的情况下不能够被B事务查询到, 但在A事务提交后却能够被B事务查询到更新后的数据, 此为不可重复读(跟①的区别在于①强调查询一次, 而②强调查询两次)
③ 幻读
场景: 在A事务查询当前数据, 根据该数据做更新操作但未提交; 同时, B事务做插入数据的操作并提交了. 此时, A事务做提交操作, 返回的插入数据的数量跟之前查询到的数据的数量不相符(比之前查询到的多了B事务插入的数据的数目), 此为幻读(相当于已经添加数据了, 肯定就不一样了, ③跟①的区别在于③强调的的是插入整行的数据, 而①强调的是更新某些数据[字段])
● 事务的隔离级别
脏读 | 不可重复读 | 幻读 | |
read uncommitted(读未提交) | √ | √ | √ |
read committed(读已提交) ==> oracle的默认 | × | √ | √ |
repeatable read(可重复读) ==> mysql的默认 | × | × | √ |
serializable(序列化) | × | × | × |
ps: 隔离级别为serializable数据库, 在操作时安全性最高, 但是效率非常低下, 因为此时如果有一个事务在操作数据库, 则其他所有的事务都将被阻塞, 只有当该操作的事务结束后(commit / rollback)才能执行其他事务
● 几个相关命令
★ 查看mysql数据库的隔离级别: select @@tx_isolation;
★ 设置数据库的字符集编码: set names <编码>;
★ 关闭自动提交: set autocommit = 0;
★ 设置当前会话的隔离级别: set session transaction isolation level <隔离级别>;
★ 设置数据库全局的隔离级别: set global transaction isolation level <隔离界别>;
● DELETE和TRUNCATE在事务中的区别
SET autocommit = 0;
START TRANSACTION;
DELETE FROM author;
ROLLBACK; //DELETE是支持回滚的,此时数据并不会删除
SET autocommit = 0;
START TRANSACTION;
TRUNCATE TABLE author;
ROLLBACK; //TRUNCATE是不支持回滚的,此时数据已经被删除
● 拓展:添加保存点,类似于还原的功能。
set autocommit = 0; //禁止自动提交
start transaction; //开启事务(默认)
DELETE FROM boys WHERE id = 1; //删除id=1的数据
SAVEPOINT aaa; //创建保存点aaa(名称随便起)
DELETE FROM boys WHERE id = 3; //删除id=3的数据
ROLLBACK TO aaa; //回滚到保存点aaa
commit / rollback; //最后选择是真正地提交还是回滚, 如果真正提交的话则id=1的数据会被真正删除
前往:Mysql学习笔记进阶