Mysql
# MySql教程
# 数据库和SQL概述
# 数据库的好处
- 实现数据持久化
- 使用完整的管理系统统一管理,易于查询
# 数据库的概念
DB DBMS SQL 数据库(database):存储数据的“仓库”。它保存了一系列有组织的数据。 数据库管理系统(Database Management System)。数据库是通过DBMS创 建和操作的容器 结构化查询语言(Structure Query Language):专门用来与数据库通信的语 言
常见的数据库管理系统:MySQL、Oracle、DB2、SqlServe
# SQL语言
数据库是不认识JAVA语言的,但是我们同样要与数据库交互,这时需要使用到数据库认识的语言SQL语句,它是数据库的代码。
结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
创建数据库、创建数据表、向数据表中添加一条条数据信息均需要使用SQL语句。
# DML
DML(Data Manipulation Language):数据操纵语句,用于添加、删除、修改、查询数据库记录,并检查数据完整性。包括如下SQL语句等:
- INSERT:添加数据到数据库中
- UPDATE:修改数据库中的数据
- DELETE:删除数据库中的数据
- SELECT:选择(查询)数据
- SELECT是SQL语言的基础,最为重要。
# DDL
DDL(Data Definition Language):数据定义语句,用于库和表的创建、修改、删除。包括如下SQL语句等:
- CREATE TABLE:创建数据库表
- ALTER TABLE:更改表结构、添加、删除、修改列长度
- DROP TABLE:删除表
- CREATE INDEX:在表上建立索引
- DROP INDEX:删除索引
# DCL
DCL(Data Control Language):数据控制语句,用于定义用户的访问权限和安全级别包括如下SQL语句等:
- GRANT:授予访问权限
- REVOKE:撤销访问权限
- COMMIT:提交事务处理
- ROLLBACK:事务处理回退
- SAVEPOINT:设置保存点
- LOCK:对数据库的特定部分进行锁定
# DQL
数据查询语言:简称DQL(Data Query Language),用来查询数据库中表的记录。关键字:select,from,where等
# mysql的安装与使用
# mysql的安装
- Windows
- Linux
# 启动和停止MySQL服务
Windows
方式一:通过计算机管理方式 右击计算机—管理—服务—启动或停止MySQL服务 方式二:通过命令行方式 启动:net start mysql服务名 停止:net stop mysql服务名
Linux
启动:service mysql(mysqld) start
停止:service mysql(mysqld) stop
状态:service mysql(mysqld) status
# MySQL服务端的登录和退出
登录
mysql –u用户名 –p密码
远程登录
mysql -h 主机名 -P 端口号 -u root -proot
退出
exit(quit)
# MySql数据库的使用
- 不区分大小写(Linux上区分,可以通过配置文件修改)
- 每句话用;或\g结尾
- 各子句一般分行写
- 关键字不能缩写也不能分行
- 用缩进提高语句的可读性
# 基本命令
查看 mysql 中有哪些个数据库:
show databases;
使用一个数据库:
use 数据库名称;
查看当前使用数据库中的表:
show tables;
查看指定数据库中的表:
show tables from 库名;
新建一个数据库:
#创建数据库 数据库中数据的编码采用的是安装数据库时指定的默认编码 utf8
CREATE DATABASE 数据库名;
#创建数据库 并指定数据库中数据的编码
CREATE DATABASE 数据库名 CHARACTER SET utf8;
2
3
4
5
查看当前选择的数据库:
select database();
创建表:
create table stuinfo(
id int,
name varchar(20));
2
3
查看表结构:
desc 表名;
查看表中的所有记录:
select * from 表名;
向表中插入记录:
insert into 表名(列名1,列名,...,列名n) values(列1值,列2值,...,列n值);
注意:插入 varchar 或 date 型的数据要用 单引号 引起来
# 基本查询
# SELECT 语句
SELECT *****|{[DISTINCT] column|expression [alias],...}FROM table
- SELECT 标识选择哪些列。
- FROM 标识从哪个表中选
语法:
select 查询列表 from 表名;
特点:
- 查询列表可以是:表中的字段、常量值、表达式、函数
- 查询的结果是一个虚拟的表格
查询全部列:
SELECT *FROM 表名;
查询特定的列:
SELECT id, name FROM student;
查询表达式:
SELECT 100%98;
查询函数:
select now();
# 查询时给列的别名
别名使用双引号,以便在别名中包含空格或特殊的字符
①便于理解 ②如果要查询的字段有重名的情况,使用别名可以区分开来
使用as
SELECT 列名1 AS "列1别名",列名2 AS "列2别名" FROM 表名;1案例:
SELECT 100%98 AS "结果"; SELECT last_name AS "姓",first_name AS "名" FROM employees;1
2使用空格
SELECT 列名1 "列1别名",列名2 "列2别名" FROM 表名;1案例:
# 查询salary,显示结果为 out put SELECT salary AS "out put" FROM employees;1
2
# 查询去重
select distinct 字段名 from 表名;
# 案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;
2
3
# +号的作用
java中的+号: ①运算符,两个操作数都为数值型 ②连接符,只要有一个操作数为字符串
mysql中的+号: 仅仅只有一个功能:运算符
# 直接运算
select 数值+数值;
# 先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算
select 字符+数值;
# 结果都为null
select null+值;
2
3
4
5
6
7
8
# concat函数
功能:拼接字符,相当于java中的 "1"+"23"="123"
select concat(字符1,字符2,字符3,...);
# IFNULL函数
功能:判断某字段或表达式是否为null,如果为null 返回指定的值,否则返回原本的值
# 如果commission_pct列为空则返回0
select ifnull(1,0) 返回1
select ifnull(null,0) 返回0
2
3
# ISNULL函数
功能:判断某字段或表达式是否为null,如果是,则返回1,否则返回0
select isnull(1) 返回0
select isnull(1/0) 返回1
2
# NULLIF函数
NULLIF(expr1,expr2) 的用法:
如果expr1 == expr2 则 NULL
否则 为 expr1
SELECT NULLIF(1,1) 返回NULL
SELECT NULLIF(2,1) 返回 2
2
3
4
5
# 条件查询
过滤:使用WHERE 子句,将不满足条件的行过滤掉。
语法:
select 查询列表 from 表名 where 筛选条件;
| 比较运算符 | >、< 、>=、<=、= 、<> | 大于、小于、大于等于、小于等于、等于、不等于 |
|---|---|---|
| BETWEEN ...AND... | 显示在某一区间的值(含头含尾) | |
| IN(set) | 显示在in列表中的值,例:in(100,200) | |
| LIKE 通配符 | 模糊查询,Like语句中有两个通配符:% 用来匹配多个字符;例first_name like ‘a%’;_ 用来匹配一个字符。例first_name like ‘a_’; | |
| IS NULL | 判断是否为空is null; 判断为空;is not null; 判断不为空 | |
| 逻辑运算符 | and (&&) | 多个条件同时成立 |
| or (||) | 多个条件任一成立 | |
| not (!) | 不成立,例:where not(salary>100); |
# 案例
# 按条件表达式筛选
查询工资>12000的员工信息
SELECT * FROM employees WHERE salary>12000;1
2
3
4
5
6查询部门编号不等于90号的员工名和部门编号
SELECT last_name, department_id FROM employees WHERE department_id<>90;1
2
3
4
5
6
7
# 按逻辑表达式筛选
查询工资在10000到20000之间的员工名、工资以及奖金
SELECT last_name, salary, commission_pct FROM employees WHERE salary>=10000 AND salary<=20000;1
2
3
4
5
6
7
8或者
SELECT last_name, salary, commission_pct FROM employees WHERE salary BETWEEN 10000 AND 20000;1
2
3
4
5
6
7
8
9查询部门编号不是在90到110之间,或者工资高于15000的员工信息
SELECT * FROM employees WHERE NOT ( department_id >= 90 AND department_id <= 110 ) OR salary > 15000;1
2
3
4
5
6
7或者
SELECT * FROM employees WHERE NOT ( department_id BETWEEN 90 AND 110 ) OR salary > 15000;1
2
3
4
5
6
7
# 模糊查询
查询员工名中包含字符a的员工信息
select * from employees where last_name like '%a%';1
2
3
4
5
6查询员工名中第三个字符为e,第五个字符为a的员工名和工资
select last_name, salary FROM employees WHERE last_name LIKE '__e_a%';1
2
3
4
5
6
7查询员工名中第二个字符为_的员工名
ESCAPE:定义转义标识
SELECT last_name FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$';1
2
3
4
5
6
# IN
含义:判断某字段的值是否属于in列表中的某一项 特点:
- 使用in提高语句简洁度
- 列表的值类型必须一致或兼容
- 列表中不支持通配符
查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT
last_name,
job_id
FROM
employees
WHERE
job_id IN( 'IT_PROT' ,'AD_VP','AD_PRES');
2
3
4
5
6
7
或者
SELECT
last_name,
job_id
FROM
employees
WHERE
job_id = 'IT_PROT' OR job_id = 'AD_VP' OR JOB_ID ='AD_PRES';
2
3
4
5
6
7
# IS NULL
=或<>不能用于判断null值 is null或is not null 可以判断null值
查询没有奖金的员工名和奖金率
SELECT last_name, commission_pct FROM employees WHERE commission_pct IS NULL;1
2
3
4
5
6
7查询有奖金的员工名和奖金率
SELECT last_name, commission_pct FROM employees WHERE commission_pct IS NOT NULL;1
2
3
4
5
6
7
# 安全等于 <=>
查询没有奖金的员工名和奖金率
SELECT last_name, commission_pct FROM employees WHERE commission_pct <=>NULL;1
2
3
4
5
6
7查询工资为12000的员工信息
SELECT last_name, salary FROM employees WHERE salary <=> 12000;1
2
3
4
5
6
7
8
IS NULL:仅仅可以判断NULL值,可读性较高,建议使用 <=> :既可以判断NULL值,又可以判断普通的数值,可读性较低
错误的写法:
SELECT
last_name,
commission_pct
FROM
employees
WHERE
salary IS 12000;
2
3
4
5
6
7
8
# 排序查询
ORDER BY
- 使用 ORDER BY 子句排序
- ASC(ascend): 升序
- DESC(descend): 降序
# 语法
select 查询列表
from 表名
【where 筛选条件】
order by 排序的字段或表达式;
2
3
4
5
6
7
8
# 特点
- asc代表的是升序,可以省略
- order by子句可以支持 单个字段、别名、表达式、函数、多个字段
- order by子句在查询语句的最后面,除了limit子句
# 案例
# 单个字段排序
查询员工表按薪水降序
SELECT * FROM employees ORDER BY salary DESC;1查询部门编号>=90的员工信息,并按员工编号降序
SELECT * FROM employees WHERE department_id>=90 ORDER BY employee_id DESC;1
2
3
4
# 按表达式排序
查询员工信息 按年薪降序
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) FROM employees ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;1
2
3
# 按别名排序
查询员工信息 按年薪升序
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪 FROM employees ORDER BY 年薪 ASC;1
2
3
# 按函数排序
查询员工名,并且按名字的长度降序
SELECT LENGTH(last_name),last_name FROM employees ORDER BY LENGTH(last_name) DESC;1
2
3
# 多个字段排序
查询员工信息,要求先按工资降序,再按employee_id升序
SELECT * FROM employees ORDER BY salary DESC,employee_id ASC;1
2
3
# 单行函数
# 字符函数
# length(s)
获取参数值的字符个数
SELECT LENGTH('john');
SELECT LENGTH('张三丰hahaha');
2
# CONCAT(s1,s2...sn)
拼接字符串
SELECT CONCAT(last_name,'_',first_name) 姓名 FROM employees;
# UPPER(s)
将字符串转换为大写
SELECT UPPER('john');
# LOWER(s)
将字符串转换为小写
SELECT LOWER('joHn');
# 示例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名 FROM employees;
2
3
# SUBSTR(s, start, length)、SUBSTRING()
SUBSTR(s, start, length):从字符串 s 的 start 位置截取长度为 length 的子字符串
截取字符串(注意:索引从1开始)
SELECT SUBSTR('李莫愁爱上了陆展元',7) out_put;
SELECT SUBSTRING('李莫愁爱上了陆展元',7) out_put;
SELECT SUBSTR('李莫愁爱上了陆展元',1,3) out_put;
# 案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) out_put
FROM employees;
2
3
4
5
6
7
# INSTR(s,s)
返回子串第一次出现的索引,如果找不到返回0
SELECT INSTR('杨不殷六侠悔爱上了殷六侠','殷八侠') AS out_put;
# TRIM(s)
LTRIM(s):去掉字符串 s 开始处的空格
RTRIM(s):去掉字符串 s 结尾处的空格
去掉字符串开始和结尾处的空格
SELECT LENGTH(TRIM(' 张翠山 ')) AS out_put;
# LPAD(s1,len,s2)
在字符串 s1 的开始处填充字符串 s2,使字符串长度达到 len
如果长度小于原字符串,则只取前len个字符
SELECT LPAD('殷素素',5,'*') AS out_put; --> **殷素素
SELECT LPAD('殷素素',2,'*') AS out_put; --> 殷素
2
# RPAD(s1,len,s2)
在字符串 s1 的结尾处添加字符串 s2,使字符串的长度达到 len
如果长度小于原字符串,则只取前len个字符
SELECT RPAD('殷素素',5,'*') AS out_put; --> 殷素素**
SELECT RPAD('殷素素',2,'*') AS out_put; --> 殷素
2
# REPLACE(s,s1,s2)
用字符串 s2 替代字符串 s 中的字符串 s1
SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏') AS out_put; -->张无忌爱上了赵敏
# 数学函数
# ROUND(x)
ROUND(x,d):保留d位小数
四舍五入
SELECT ROUND(-1.55); --> -2
SELECT ROUND(1.567,2); --> 1.57
2
# CEIL(x)
向上取整
SELECT CEIL(-1.02); --> -1
SELECT CEIL(1.02); --> 2
2
# FLOOR(x)
向下取整
SELECT FLOOR(-1.58); --> -2
SELECT FLOOR(1.58); --> 1
2
# TRUNCATE(x,y)
返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入)
SELECT TRUNCATE(1.567,2); --> 1.56
# MOD(x,y)
返回 x 除以 y 以后的余数
SELECT MOD(10,-3); --> 1
等价于
SELECT 10%-3; --> 1
余数的符号取决于被除数的符号
SELECT MOD(-10,3); --> -1
SELECT MOD(-10,-3); --> -1
2
# 日期函数
# NOW()
返回当前系统时间(注:日期+时间)
SELECT NOW(); --> 2019-10-05 09:56:57
# CURDATE()
返回当前系统日期,不包含时间
SELECT CURDATE(); --> 2019-10-05
# CURTIME()
SELECT CURTIME(); -->09:56:57
# YEAR(d)
返回年份
SELECT YEAR(NOW()); -->2019
SELECT YEAR('1998-1-1'); -->1998
SELECT YEAR(hiredate) 入职时间 FROM employees;
2
3
# MONTH(d)
返回日期d中的月份值,1 到 12
SELECT MONTH(NOW()); --> 10
# MONTHNAME(d)
返回日期当中的月份名称,如 November
SELECT MONTHNAME(NOW()); --> October
# STR_TO_DATE(s, f)
将字符通过指定的格式转换成日期
SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS out_put; --> 1998-03-02
查询入职日期为1992-4-3的员工信息
SELECT * FROM employees WHERE hiredate = '1992-4-3';
# 或者
SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');
2
3
4
5
# DATE_FORMAT(d,f)
将日期通过指定的格式转换成字符
SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日') AS out_put; --> 2019年10月05日
查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期
FROM employees
WHERE commission_pct IS NOT NULL;
2
3
# 其他函数
# VERSION()
返回数据库的版本号
SELECT VERSION();
# DATABASE()
返回当前数据库名
SELECT DATABASE();
# USER()
返回当前用户
SELECT USER();
# 流程控制函数
# IF(expr,v1,v2)
如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。
SELECT IF(1 > 0,'正确','错误'); --> 正确
SELECT
last_name,
commission_pct,
IF
( commission_pct IS NULL, '没奖金,呵呵', '有奖金,嘻嘻' ) 备注
FROM
employees;
2
3
4
5
6
7
8
# CASE
CASE expression
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
WHEN conditionN THEN resultN
ELSE result
END
2
3
4
5
6
7
CASE 表示函数开始,END 表示函数结束。如果 condition1 成立,则返回 result1, 如果 condition2 成立,则返回 result2,当全部不成立则返回 result,而当有一个成立之后,后面的就不执行了。
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end
SELECT CASE
WHEN 1 > 0
THEN '1 > 0'
WHEN 2 > 0
THEN '2 > 0'
ELSE '3 > 0'
END
--> 1 > 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
查询员工的工资,要求
- 部门号=30,显示的工资为1.1倍
- 部门号=40,显示的工资为1.2倍
- 部门号=50,显示的工资为1.3倍
- 其他部门,显示的工资为原工资
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;
2
3
4
5
6
7
8
查询员工的工资的情况
- 如果工资>20000,显示A级别
- 如果工资>15000,显示B级别
- 如果工资>10000,显示C级别
- 否则,显示D级别
SELECT salary,
CASE
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM employees;
2
3
4
5
6
7
8
# 分组函数
用作统计使用,又称为聚合函数或统计函数或组函数
# SUM(expression)
求和
SELECT SUM(salary) FROM employees;
# AVG(expression)
平均值
SELECT AVG(salary) FROM employees;
# MAX(expression)
最大值
SELECT MAX(salary) FROM employees;
# MIN(expression)
最小值
SELECT MIN(salary) FROM employees;
# COUNT(expression)
计算个数
SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
2
3
效率: MYISAM存储引擎下 ,COUNT()的效率高 INNODB存储引擎下,COUNT()和COUNT(1)的效率差不多,比COUNT(字段)要高一些
SELECT
SUM( salary ) 和,
AVG( salary ) 平均,
MAX( salary ) 最高,
MIN( salary ) 最低,
COUNT( salary ) 个数
FROM
employees;
2
3
4
5
6
7
8
平均工资保留两位小数(四舍五入)
SELECT
SUM( salary ) 和,
ROUND( AVG( salary ), 2 ) 平均,
MAX( salary ) 最高,
MIN( salary ) 最低,
COUNT( salary ) 个数
FROM
employees;
2
3
4
5
6
7
8
# 参数支持哪些类型
# 字符
先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算
SELECT
SUM( last_name ),
AVG( last_name )
FROM
employees;
--> 0,0
2
3
4
5
6
7
与排序差不多
SELECT
MAX( last_name ),
MIN( last_name )
FROM
employees;
2
3
4
5
# 日期
SELECT SUM(hiredate) ,AVG(hiredate) FROM employees;
SELECT MAX(hiredate),MIN(hiredate) FROM employees;
2
# 特点
- sum、avg一般用于处理数值型
- max、min、count可以处理任何类型
- 以上分组函数都忽略null值
- 可以和distinct搭配实现去重的运算
- 一般使用count(*)用作统计行数
- 和分组函数一同查询的字段要求是group by后的字段
# 案例
查询员工表中的最大入职时间和最小入职时间的相差天数 (DIFFRENCE)
SELECT MAX( hiredate ) 最大, MIN( hiredate ) 最小, (MAX( hiredate )- MIN( hiredate ))/ 1000 / 3600 / 24 DIFFRENCE FROM employees;1
2
3
4
5
6使用DATEDIFF(d1,d2)函数,计算日期 d1~d2 之间相隔的天数
SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) DIFFRENCE;1查询部门编号为90的员工个数
SELECT COUNT(*) FROM employees WHERE department_id = 90;1
2
3
4
5
6
# 分组查询
# 语法
select 分组函数,分组后的字段
from 表
【where 筛选条件】
group by 分组的字段
【having 分组后的筛选】
【order by 排序列表】
2
3
4
5
6
# 特点
- 和分组函数一同查询的字段
必须是group by后出现的字段 - 筛选分为两类:分组前筛选和分组后筛选
| 筛选 | 针对的表 | 连接的关键字 |
|---|---|---|
| 分组前筛选 | 原始表 | where |
| 分组后筛选 | group by后的结果集 | having |
# 问题
分组函数做筛选能不能放在where后面
不能,原表中没有分组后的数据
# 案例
# 简单的分组
查询每个工种的员工平均工资
SELECT AVG(salary),job_id FROM employees GROUP BY job_id;1
2
3查询每个位置的部门个数
SELECT COUNT(*),location_id FROM departments GROUP BY location_id;1
2
3
# 分组前筛选
查询邮箱中包含a字符的 每个部门的最高工资
SELECT MAX(salary),department_id FROM employees WHERE email LIKE '%a%' GROUP BY department_id;1
2
3
4查询有奖金的每个领导手下员工的平均工资
SELECT AVG( salary ), manager_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY manager_id;1
2
3
4
5
6
7
8
9
# 分组后筛选
查询哪个部门的员工个数>5
① 首先查询每个部门的员工个数
SELECT COUNT(*),department_id FROM employees GROUP BY department_id;1
2
3② 筛选刚才①结果
SELECT COUNT(*),department_id FROM employees GROUP BY department_id HAVING COUNT(*)>5;1
2
3
4
5
6每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT MAX( salary ) 最高工资, job_id 工种编号 FROM employees WHERE commission_pct IS NOT NULL GROUP BY job_id HAVING MAX( salary )> 12000;1
2
3
4
5
6
7
8
9
10
11领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资
SELECT manager_id 领导编号, MIN( salary ) 最低工资 FROM employees WHERE manager_id > 102 GROUP BY manager_id HAVING MIN( salary )> 50001
2
3
4
5
6
7
8
9
10
11
# 添加排序
每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
SELECT job_id 工种编号, MAX( salary ) 最高工资 FROM employees WHERE commission_pct IS NOT NULL GROUP BY job_id HAVING MAX( salary )> 6000 ORDER BY MAX( salary );1
2
3
4
5
6
7
8
9
10
11
12
13
# 按多个字段分组
查询每个工种每个部门的最低工资,并按最低工资降序
SELECT MIN( salary ) 最低工资, department_id 部门, job_id 工种 FROM employees GROUP BY department_id, job_id ORDER BY MIN( salary ) DESC;1
2
3
4
5
6
7
8
9
10
11
# 连接查询
# 含义
又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
select 字段1,字段2
from 表1,表2,...;
2
3
# 笛卡尔乘积现象
当查询多个表时,没有添加有效的连接条件,导致多个表所有行实现完全连接
表1 有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件 如何避免:添加有效的连接条件
# 分类
按年代分类: sql92标准:仅仅支持内连接 sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
按功能分类:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接
右外连接
全外连接(mysql不支持)
交叉连接
2
3
4
5
6
7
8
9
10
11
# 等值连接
- 多表等值连接的结果为多表的交集部分
- n表连接,至少需要n-1个连接条件
- 表的顺序没有要求
- 一般需要为表起别名
- 可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
# 案例1
查询女神名和对应的男神名
SELECT NAME,boyName FROM boys, beauty WHERE beauty.boyfriend_id = boys.id;1
2
3
4
5
6查询员工名和对应的部门名
SELECT last_name, department_name FROM employees, departments WHERE employees.`department_id` = departments.`department_id`;1
2
3
4
5
6
7
8
# 为表起别名
- 提高语句的简洁度
- 区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定
查询员工名、工种号、工种名
SELECT e.last_name, e.job_id, j.job_title FROM employees e, jobs j WHERE e.`job_id` = j.`job_id`;1
2
3
4
5
6
7
8
9
# 加筛选条件
查询有奖金的员工名、部门名
SELECT last_name, department_name, commission_pct FROM employees e, departments d WHERE e.`department_id` = d.`department_id` AND e.`commission_pct` IS NOT NULL;1
2
3
4
5
6
7
8
9
10查询城市名中第二个字符为o的部门名和城市名
SELECT department_name, city FROM departments d, locations l WHERE d.`location_id` = l.`location_id` AND city LIKE '_o%';1
2
3
4
5
6
7
8
9
# 加分组
查询每个城市的部门个数
SELECT COUNT(*) 个数, city FROM departments d, locations l WHERE d.`location_id` = l.`location_id` GROUP BY city;1
2
3
4
5
6
7
8
9
10查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT department_name, d.`manager_id`, MIN( salary ) FROM departments d, employees e WHERE d.`department_id` = e.`department_id` AND commission_pct IS NOT NULL GROUP BY department_name, d.`manager_id`;1
2
3
4
5
6
7
8
9
10
11
12
13查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT job_title, COUNT(*) FROM employees e, jobs j WHERE e.`job_id` = j.`job_id` GROUP BY job_title ORDER BY COUNT(*) DESC;1
2
3
4
5
6
7
8
9
10
11
12查询员工名、部门名和所在的城市,并且城市名以s开头,按部门名称降序
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 city LIKE 's%' ORDER BY department_name DESC;1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 非等值连接
# 语法
select 查询列表
from 表1 别名,表2 别名
where 非等值的连接条件
【and 筛选条件】
【group by 分组字段】
【having 分组后的筛选】
【order by 排序字段】
2
3
4
5
6
7
# 案例2
查询员工的工资和工资级别
SELECT e.salary 工资, j.grade_level 工资级别 FROM employees e, job_grades j WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;1
2
3
4
5
6
7
8
9查询员工的工资和工资级别并筛选出级别为A的
SELECT e.salary 工资, j.grade_level 工资级别 FROM employees e, job_grades j WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal AND j.grade_level = 'A';1
2
3
4
5
6
7
8
9
10
# 自连接
# 语法
select 查询列表
from 表 别名1,表 别名2
where 等值的连接条件
【and 筛选条件】
【group by 分组字段】
【having 分组后的筛选】
【order by 排序字段】
2
3
4
5
6
7
查询员工名和上级的名称
SELECT e.last_name 员工名, m.last_name 上级名称 FROM employees e, employees m WHERE e.manager_id = m.employee_id;1
2
3
4
5
6
7
8
# SQL99语法-内连接
select 查询列表
from 表1 别名
【inner】 join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句;
2
3
4
5
6
7
8
# 特点
- 表的顺序可以调换
- 内连接的结果=多表的交集
- n表连接至少需要n-1个连接条件
- 添加排序、分组、筛选
- inner可以省略
- 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
- inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
# 分类
- 等值连接
- 非等值连接
- 自连接
# 内连接-等值连接
查询员工名、部门名
SELECT last_name, department_name FROM departments d INNER JOIN employees e ON e.`department_id` = d.`department_id`;1
2
3
4
5
6查询名字中包含e的员工名和工种名(添加筛选)
SELECT last_name, job_title FROM employees e JOIN jobs j ON e.`job_id` = j.`job_id` WHERE e.`last_name` LIKE '%e%';1
2
3
4
5
6
7
8查询部门个数>3的城市名和部门个数,(添加分组+筛选)
SELECT COUNT( department_id ) 部门个数, city 城市名 FROM departments d JOIN locations l ON d.location_id = l.location_id GROUP BY l.city HAVING COUNT( department_id )> 3;1
2
3
4
5
6
7
8
9
10查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)
SELECT COUNT(*) 员工个数, d.department_name 部门名称 FROM departments d JOIN employees e ON d.department_id = e.department_id GROUP BY d.department_id HAVING COUNT( *)> 3 ORDER BY 1 DESC1
2
3
4
5
6
7
8
9
10
11
12
13查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
SELECT e.last_name 员工名, d.department_name 部门号, j.job_title 工种名 FROM employees e JOIN departments d ON e.department_id = d.department_id JOIN jobs j ON e.job_id = j.job_id ORDER BY d.department_name DESC;1
2
3
4
5
6
7
8
9
10
# 内连接-非等值连接
查询员工的工资级别
SELECT e.last_name 员工名, e.salary 薪水, j.grade_level 工资级别 FROM employees e JOIN job_grades j ON e.salary BETWEEN j.lowest_sal AND j.highest_sal;1
2
3
4
5
6
7
8查询工资级别的个数>20的个数,并且按工资级别降序
SELECT j.grade_level 工资级别, COUNT( * ) 个数 FROM employees e JOIN job_grades j ON e.salary BETWEEN j.lowest_sal AND j.highest_sal GROUP BY j.grade_level HAVING COUNT( * ) > 20 ORDER BY 2 DESC;1
2
3
4
5
6
7
8
9
10
11
12
13
# 内连接-自连接
查询员工的名字、上级的名字
SELECT e.last_name 员工名, m.last_name 上级名 FROM employees e JOIN employees m ON e.manager_id = m.employee_id;1
2
3
4
5
6查询姓名中包含字符k的员工的名字、上级的名字
SELECT e.last_name 员工名, m.last_name 上级名 FROM employees e JOIN employees m ON e.manager_id = m.employee_id WHERE e.last_name LIKE '%k%'1
2
3
4
5
6
7
8
# SQL99语法-外连接
# 语法
select 查询列表
from 表1 别名
left|right|full【outer】 join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句;
2
3
4
5
6
7
8
# 应用场景
用于查询一个表中有,另一个表没有的记录
# 特点
外连接的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示null
外连接查询结果=内连接结果+主表中有而从表没有的记录
左外连接,
left join左边的是主表右外连接,
right join右边的是主表左外和右外交换两个表的顺序,可以实现同样的效果
全外连接=内连接的结果+表1中有但表2没有的+表2中有但表1没有的
# 左外连接
查询哪个部门没有员工
SELECT d.* FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id WHERE e.department_id IS NULL1
2
3
4
5
6
7
# 右外连接
查询哪个部门没有员工(调换位置使用RIGHT JOIN)
SELECT d.* FROM employees e RIGHT JOIN departments d ON d.department_id = e.department_id WHERE e.department_id IS NULL1
2
3
4
5
6
7
# 全外连接
mysql不支持全外连接
USE girls;
SELECT b.*,bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.`boyfriend_id` = bo.id;
2
3
4
5
同样boys表有的beauty没有的也是显示null
# 交叉连接
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
SELECT b.*,bo.*
FROM beauty b
CROSS JOIN boys bo;
2
3
# 子查询
# 含义
嵌套在其他语句内部的select语句称为子查询或内查询, 外面的语句可以是insert、update、delete、select等,一般select作为外面语句较多 外面如果为select语句,则此语句称为外查询或主查询
# 分类
# 按出现位置
select后面:
仅仅支持标量子查询
from后面:
表子查询
where或having后面:
标量子查询
列子查询
行子查询
exists后面:
标量子查询
列子查询
行子查询
表子查询
2
3
4
5
6
7
8
9
10
11
12
13
# 按结果集的行列
标量子查询(单行子查询):结果集为一行一列 列子查询(多行子查询):结果集为多行一列 行子查询:结果集为多行多列 表子查询:结果集为多行多列
# 示例
# where或having后面
- 标量子查询(单行单列)
- 列子查询(多行单列)
- 行子查询(多行多列)
# 特点
① 子查询放在小括号内 ② 子查询一般放在条件的右侧 ③ 标量子查询,一般搭配着单行操作符使用
<、>、<=、>=、=、<>
④ 列子查询,一般搭配着多行操作符使用
in、any/some、all
⑤ 子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
# 标量子查询
谁的工资比 Abel 高?
① 查询Abel的工资
SELECT salary FROM employees WHERE last_name = 'Abel'1
2
3②查询员工的信息,满足 salary>①结果
SELECT * FROM employees WHERE salary>( SELECT salary FROM employees WHERE last_name = 'Abel' );1
2
3
4
5
6
7
8
9返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资
① 查询141号员工的job_id
SELECT job_id FROM employees WHERE employee_id = 1411
2
3② 查询143号员工的salary
SELECT salary FROM employees WHERE employee_id = 1431
2
3③ 查询员工的姓名,job_id 和工资,要求job_id=①并且salary>②
SELECT last_name,job_id,salary FROM employees WHERE job_id = ( SELECT job_id FROM employees WHERE employee_id = 141 ) AND salary>( SELECT salary FROM employees WHERE employee_id = 143 );1
2
3
4
5
6
7
8
9
10
11
12返回公司工资最少的员工的last_name,job_id和salary
① 查询公司的 最低工资
SELECT MIN(salary) FROM employees1
2② 查询last_name,job_id和salary,要求salary=①
SELECT last_name,job_id,salary FROM employees WHERE salary=( SELECT MIN(salary) FROM employees );1
2
3
4
5
6查询最低工资大于50号部门最低工资的部门id和其最低工资
① 查询50号部门的最低工资
SELECT MIN(salary) FROM employees WHERE department_id = 501
2
3② 查询每个部门的最低工资
SELECT MIN(salary),department_id FROM employees GROUP BY department_id1
2
3③ 在②基础上筛选,满足min(salary)>①
SELECT MIN(salary),department_id FROM employees GROUP BY department_id HAVING MIN(salary)>( SELECT MIN(salary) FROM employees WHERE department_id = 50 );1
2
3
4
5
6
7
8非法使用标量子查询
SELECT MIN(salary),department_id FROM employees GROUP BY department_id HAVING MIN(salary)>( SELECT salary FROM employees WHERE department_id = 250 );这里250号部门的员工可能不止一个人,查询的是多行单列,就是列子查询了
# 列子查询
| 操作符 | 含义 |
|---|---|
| IN/NOT IN | 等于列表中的任意一个 |
| ANY|SOME | 和子查询返回的某一个值比较 |
| ALL | 和子查询返回的所有值比较 |
返回location_id是1400或1700的部门中的所有员工姓名
①查询location_id是1400或1700的
部门编号SELECT DISTINCT department_id FROM departments WHERE location_id IN(1400,1700)1
2
3② 查询员工姓名,要求部门号是①列表中的某一个
SELECT last_name FROM employees WHERE department_id IN( SELECT DISTINCT department_id FROM departments WHERE location_id IN(1400,1700) );1
2
3
4
5
6
7返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary
① 查询job_id为‘IT_PROG’部门任一工资
SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG';1
2
3② 查询员工号、姓名、job_id 以及salary,salary<(①)的任意一个
SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary<ANY( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) AND job_id<>'IT_PROG';1
2
3
4
5
6
7
8或者(小于最大值,就肯定满足任意一个)
SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary<( SELECT MAX(salary) FROM employees WHERE job_id = 'IT_PROG' ) AND job_id<>'IT_PROG';1
2
3
4
5
6
7
8返回其它部门中比job_id为‘IT_PROG’部门所有工资都低的员工 的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary<ALL( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) AND job_id<>'IT_PROG';1
2
3
4
5
6
7
8或者(小于最小的)
SELECT last_name,employee_id,job_id,salary FROM employees WHERE salary<( SELECT MIN( salary) FROM employees WHERE job_id = 'IT_PROG' ) AND job_id<>'IT_PROG';1
2
3
4
5
6
7
8
# 行子查询
查询员工编号最小并且工资最高的员工信息
SELECT * FROM employees WHERE (employee_id,salary)=( SELECT MIN(employee_id),MAX(salary) FROM employees );1
2
3
4
5
6或者
SELECT * FROM employees WHERE employee_id=( SELECT MIN(employee_id) FROM employees )AND salary=( SELECT MAX(salary) FROM employees );1
2
3
4
5
6
7
8
9
# select后面
仅仅支持标量子查询
查询每个部门的员工个数
SELECT d.*,( SELECT COUNT(*) FROM employees e WHERE e.department_id = d.`department_id` ) 个数 FROM departments d;1
2
3
4
5
6或者(使用外连接)
SELECT d.*, count( e.employee_id ) 个数 FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id GROUP BY d.department_id;1
2
3
4
5
6
7
8查询员工号=102的部门名
SELECT department_id,( SELECT department_name FROM departments d WHERE department_id = e.department_id ) FROM employees e WHERE e.employee_id = 102;1
2
3
4
5
6
7
8
9或者
SELECT department_name,e.department_id FROM departments d INNER JOIN employees e ON d.department_id=e.department_id WHERE e.employee_id=102;1
2
3
4
5# from后面
将子查询结果充当一张表,要求必须起别名
查询每个部门的平均工资的工资等级
SELECT ag_dep.*,g.`grade_level` FROM ( SELECT AVG(salary) ag,department_id FROM employees GROUP BY department_id ) ag_dep INNER JOIN job_grades g ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;1
2
3
4
5
6
7
8
# exists后面(相关子查询)
判断子查询是否存在结果,存在返回1,不能存在返回0,可用于筛选判断
查询有员工的部门名
SELECT department_name FROM departments d WHERE EXISTS( SELECT * FROM employees e WHERE d.`department_id`=e.`department_id` );1
2
3
4
5
6
7查询没有女朋友的男神信息
SELECT bo.* FROM boys bo WHERE NOT EXISTS( SELECT boyfriend_id FROM beauty b WHERE bo.`id`=b.`boyfriend_id` );1
2
3
4
5
6
7
8SELECT bo.* FROM boys bo WHERE bo.id NOT IN( SELECT boyfriend_id FROM beauty )1
2
3
4
5
6
能用exists的地方就都可以用In替代
# 分页查询
# 应用场景
当要显示的数据,一页显示不全,需要分页提交sql请求
# 语法
select 查询列表
from 表
【join type】 join 表2
on 连接条件
where 筛选条件
group by 分组字段
having 分组后的筛选
order by 排序的字段】
limit 【offset,】size;
2
3
4
5
6
7
8
9
offset要显示条目的起始索引(起始索引从0开始)
size 要显示的条目个数
# 分页公式
(page-1)*size,size
# 案例
1.查询前五条员工信息
SELECT * FROM employees LIMIT 0,5;
# 或者
SELECT * FROM employees LIMIT 5;
2
3
2.查询第11条~第25条
SELECT * FROM employees LIMIT 10,15;
3.有奖金的员工信息,并且工资较高的前10名显示出来
SELECT
*
FROM
employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 10;
2
3
4
5
6
7
合并、联合,将多次查询结果合并成一个结果
# 语法
查询语句1
union 【all】
查询语句2
union 【all】
...
2
3
4
5
# 意义
- 将一条比较复杂的查询语句拆分成多条语句
- 适用于查询多个表的时候,查询的列基本是一致
# 特点
- 要求多条查询语句的查询列数是一致的!
- 要求多条查询语句的查询的每一列的类型和顺序最好一致
- union关键字默认去重,如果使用union all 可以包含重复项
# 案例
查询部门编号>90或邮箱包含a的员工信息
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;1SELECT * FROM employees WHERE email LIKE '%a%' UNION SELECT * FROM employees WHERE department_id>90;1
2
3
# DML数据处理之增删改
DML(Data Manipulation Language –数据操纵语言) 可以在下列条件下执行:
- 向表中插入数据
- 修改现存数据
- 删除现存数据
事务是由完成若干项工作的DML语句组成的
运行以下脚本创建表my_employees
USE myemployees;
CREATE TABLE my_employees(
Id INT(10),
First_name VARCHAR(10),
Last_name VARCHAR(10),
Userid VARCHAR(10),
Salary DOUBLE(10,2)
);
CREATE TABLE users(
id INT,
userid VARCHAR(10),
department_id INT
);
2
3
4
5
6
7
8
9
10
11
12
13
14
显示表my_employees的结构
DESC my_employees;
# INSERT
# 方式一
# 语法
insert into 表名(字段名,...) values(值,...);
# 特点
要求值的类型和字段的类型要一致或兼容
字段的个数和顺序不一定与原始表中的字段个数和顺序一致
但必须保证值和字段一一对应假如表中有可以为null的字段,注意可以通过以下两种方式插入null值
① 字段和值都省略 ② 字段写上,值使用null
字段和值的个数必须一致
字段名可以省略,默认所有列
# 方式二
语法
insert into 表名 set 字段=值,字段=值,...;
两种方式的区别:
方式一支持一次插入多行,语法如下:
insert into 表名【(字段名,..)】 values(值,..),(值,...),...;1方式一支持子查询,语法如下:
insert into 表名 查询语句;1
2
# 向表中插入数据
向my_employees表中插入下列数据
| ID | FIRST_NAME | LAST_NAME | USERID | SALARY |
|---|---|---|---|---|
| 1 | patel | Ralph | Rpatel | 895 |
| 2 | Dancs | Betty | Bdancs | 860 |
| 3 | Biri | Ben | Bbiri | 1100 |
| 4 | Newman | Chad | Cnewman | 750 |
| 5 | Ropeburn | Audrey | Aropebur | 155 |
INSERT INTO my_employees
VALUES
( 1, 'patel', 'Ralph', 'Rpatel', 895 ),
( 2, 'Dancs', 'Betty', 'Bdancs', 860 ),
( 3, 'Biri', 'Ben', 'Bbiri', 1100 ),
( 4, 'Newman', 'Chad', 'Cnewman', 750 ),
( 5, 'Ropeburn', 'Audrey', 'Aropebur', 1550 );
2
3
4
5
6
7
或者
# 先删除表数据
DELETE FROM my_employees;
# 再插入
INSERT INTO my_employees
SELECT 1,'patel','Ralph','Rpatel',895 UNION
SELECT 2,'Dancs','Betty','Bdancs',860 UNION
SELECT 3,'Biri','Ben','Bbiri',1100 UNION
SELECT 4,'Newman','Chad','Cnewman',750 UNION
SELECT 5,'Ropeburn','Audrey','Aropebur',1550;
2
3
4
5
6
7
8
9
向users表中插入数据
INSERT INTO users
VALUES
( 1, 'Rpatel', 10 ),
( 2, 'Bdancs', 10 ),
( 3, 'Bbiri', 20 );
2
3
4
5
# UPDATE
# 修改单表的记录
# 语法
update 表名 set 字段=值,字段=值 【where 筛选条件】;
将3号员工的last_name修改为“drelxer”
UPDATE my_employees SET last_name='drelxer' WHERE id = 3;1将所有工资少于900的员工的工资修改为1000
UPDATE my_employees SET salary=1000 WHERE salary<900;1
# 修改多表的记录
# 语法
update 表1 别名
left|right|inner join 表2 别名
on 连接条件
set 字段=值,字段=值
【where 筛选条件】;
2
3
4
5
# DELETE
# 删除单表的记录
# 语法
delete from 表名 【where 筛选条件】【limit 条目数】
# 级联删除
# 语法
delete 别名1,别名2 from 表1 别名
inner|left|right join 表2 别名
on 连接条件
【where 筛选条件】
2
3
4
将userid 为Bbiri的user表和my_employees表的记录全部删除
DELETE u,e FROM users u JOIN my_employees e ON u.`userid` = e.`Userid` WHERE u.`userid` = 'Bbiri';1
2
3
4
5
6
7删除所有数据
DELETE FROM my_employees; DELETE FROM users;1
2
# TRUNCATE
清空表
# 语法
truncate table 表名
# DELETE和TRUNCATE的区别
- truncate删除后,如果再插入,标识列从1开始。delete删除后,如果再插入,标识列从断点开始
- delete可以添加筛选条件,truncate不可以添加筛选条件
- truncate效率较高
- truncate没有返回值,delete可以返回受影响的行数
- truncate不可以回滚,delete可以回滚
# DDL数据库定义语言
数据定义语言:简称DDL(Data Definition Language),用来定义数据库对象:数据库,表,列等。关键字:create,alter,drop等
- 创建: create
- 修改: alter
- 删除: drop
# 库的管理
# 创建库
create database 【if not exists】 库名【 character set 字符集名】;
CREATE DATABASE IF NOT EXISTS books;
2
# 修改库
alter database 库名 character set 字符集名;
# 修改库名
已废弃:RENAME DATABASE books TO 新库名;
# 修改库的字符集
ALTER DATABASE books CHARACTER SET gbk;
# 删除库
drop database 【if exists】 库名;
# 表的管理
# 创建表
create table 表名(
列名 列的类型【(长度) 约束】,
列名 列的类型【(长度) 约束】,
列名 列的类型【(长度) 约束】,
...
列名 列的类型【(长度) 约束】
)
2
3
4
5
6
7
创建表Book表
CREATE TABLE book (
id INT AUTO_INCREMENT PRIMARY KEY, #编号
bName VARCHAR ( 20 ), #图书名
price DOUBLE, #价格
authorId INT, #作者编号
publishDate DATETIME #出版日期
);
CREATE TABLE college_professional(
cp_id INT AUTO_INCREMENT PRIMARY KEY,
c_id INT ,
p_id INT ,
CONSTRAINT fk_college_id FOREIGN KEY(c_id) REFERENCES college(c_id)
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
查看book表结构
DESC book;
创建表author
CREATE TABLE IF NOT EXISTS author(
id INT,
au_name VARCHAR(20),
nation VARCHAR(10)
)
2
3
4
5
数据类型
# 修改表
# 添加列
alter table 表名 add column 列名 类型 【first|after 字段名】;
# 修改列的类型或约束
alter table 表名 modify column 列名 新类型 【新约束】;
ALTER TABLE college_professional ADD CONSTRAINT fk_professional_id FOREIGN KEY(p_id) REFERENCES professional(p_id)
2
3
# 修改列名
alter table 表名 change column 旧列名 新列名 类型;
# 删除列
alter table 表名 drop column 列名;
# 修改表名
alter table 表名 rename 【to】 新表名;
# 删除表
drop table【if exists】 表名;
# 复制表
# 复制表的结构
create table 表名 like 旧表;
# 复制表的结构+数据
create table 表名
select 查询列表 from 旧表【where 筛选】;
2
案例
向author表插入数据
INSERT INTO author VALUES ( 1, '村上春树', '日本' ), ( 2, '莫言', '中国' ), ( 3, '冯唐', '中国' ), ( 4, '金庸', '中国' );1
2
3
4
5
6创建表copy,复制author表的结构
CREATE TABLE copy LIKE author;1创建表copy2,复制author表的结构和数据
CREATE TABLE copy2 SELECT * FROM author;1
2创建表copy3,复制author表的
部分数据CREATE TABLE copy3 SELECT id,au_name FROM author WHERE nation='中国';1
2
3
4创建表copy4,复制author表的
部分字段CREATE TABLE copy4 SELECT id,au_name FROM author WHERE 0;1
2
3
4
# MYSQL数据类型
| 分类 | 类型名称 | 说明 |
|---|---|---|
| 整数类型 | tinyInt | 很小的整数 |
| smallint | 小的整数 | |
| mediumint | 中等大小的整数 | |
| int(integer) | 普通大小的整数 | |
| 小数类型 | float | 单精度浮点数 |
| double | 双精度浮点数 | |
| decimal(m,d) | 压缩严格的定点数 | |
| 日期类型 | year | YYYY 1901~2155 |
| time | HH : MM : SS -838:59 : 59~838 : 59 : 59 | |
| date | YYYY-MM-DD 1000-01-01~9999-12-3 | |
| datetime | YYYY-MM-DD HH : MM : SS 1000-01-01 00 : 00 : 00~ 9999-12-31 23 : 59 : 59 | |
| timestamp | YYYY-MM-DD HH : MM : SS 1970 | |
| 文本、二进制类型 | CHAR(M) | M为0~255之间的整数 |
| VARCHAR(M) | M为0~65535之间的整数 | |
| TINYBLOB | 允许长度0~255字节 | |
| BLOB | 允许长度0~65535字节 | |
| MEDIUMBLOB | 允许长度0~167772150字节 | |
| LONGBLOB | 允许长度0~4294967295字节 | |
| TINYTEXT | 允许长度0~255字节 | |
| TEXT | 允许长度0~65535字节 | |
| MEDIUMTEXT | 允许长度0~167772150字节 | |
| LONGTEXT | 允许长度0~4294967295字节 | |
| VARBINARY(M) | 允许长度0~M个字节的变长字节字符串 | |
| BINARY(M) | 允许长度0~M个字节的定长字节字符串 |
# 数值型
# 整型
tinyint、smallint、mediumint、int/integer、bigint
特点:
- 都可以设置无符号和有符号,默认有符号,通过unsigned设置无符号
- 如果超出了范围,会报out or range异常,插入临界值
- 长度可以不指定,默认会有一个长度,长度代表显示的最大宽度,如果不够则左边用0填充,但需要搭配zerofill,并且默认变为无符号整型
如何设置无符号
CREATE TABLE tab_int(
t1 INT,
t2 INT unsigned
);
2
3
4
显示长度
CREATE TABLE tab_int(
t1 INT(7) ZEROFILL,
t2 INT(7) ZEROFILL
);
2
3
4
# 浮点型
定点数:decimal(M,D) 浮点数: float(M,D) double(M,D)
特点:
- M代表整数部位+小数部位的个数,D代表小数部位
- 如果超出范围,则报out or range异常,并且插入临界值
- M和D都可以省略,但对于定点数,M默认为10,D默认为0
- 如果精度要求较高,则优先考虑使用定点数
# 字符型
char、varchar、binary、varbinary、enum(用于保存枚举)、set(用于保存集合)、text、blob
char:固定长度的字符,写法为char(M),最大长度不能超过M,其中M可以省略,默认为1
varchar:可变长度的字符,写法为varchar(M),最大长度不能超过M,其中M不可以省略
Enum:又称为枚举类型哦,要求插入的值必须属于列表中指定的值之一。
Set:和Enum类型类似,里面可以保存0~64个成员。和Enum类型最大的区别是:SET类型一次可以选取多个成员,而Enum只能选一个
CREATE TABLE tab_char(
c1 ENUM('a','b','c')
);
INSERT INTO tab_char VALUES('a');
INSERT INTO tab_char VALUES('b');
INSERT INTO tab_char VALUES('c');
INSERT INTO tab_char VALUES('m'); --> 会插入空字符 ''
INSERT INTO tab_char VALUES('A'); --> 会插入 'a'
CREATE TABLE tab_set(
s1 SET('a','b','c','d')
);
INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('A,B');
INSERT INTO tab_set VALUES('a,c,d');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 日期型
year年 date日期 time时间 datetime 日期+时间 timestamp 日期+时间 ,比较容易受时区、语法模式、版本的影响,更能反映当前时区的真实时间
# 常见的约束
- NOT NULL:非空,该字段的值必填
- UNIQUE:唯一,该字段的值不可重复
- DEFAULT:默认,该字段的值不用手动插入有默认值
- CHECK:检查,mysql不支持(但是设置并不会报错,只是没有效果)
- PRIMARY KEY:主键,该字段的值不可重复并且非空 unique+not null
- FOREIGN KEY:外键,该字段的值引用了另外的表的字段
添加约束的时机:
- 创建表时
- 修改表时
约束的添加分类:
列级约束:
六大约束语法上都支持,但外键约束没有效果
表级约束:
除了非空、默认,其他的都支持
2
3
4
5
6
7
# 主键和唯一
- 一个表至多有一个主键,但可以有多个唯一
- 主键不允许为空,唯一可以为空
| 保证唯一性 | 是否允许为空 | 一个表中可以有多少个 | 是否允许组合 | |
|---|---|---|---|---|
| 主键 | √ | × | 至多有1个 | √,但不推荐 |
| 唯一 | √ | √ | 可以有多个 | √,但不推荐 |
外键:
- 要求在从表设置外键关系
- 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
- 主表的关联列
必须是一个key(一般是主键或唯一) - 插入数据时,先插入主表,再插入从表
- 删除数据时,先删除从表,再删除主表
# 创建表时添加约束
CREATE TABLE 表名(
字段名 字段类型 列级约束,
字段名 字段类型,
表级约束
)
2
3
4
5
# 添加列级约束
直接在字段名和类型后面追加 约束类型即可。
只支持:默认、非空、主键、唯一
# 创建一个数据库
CREATE DATABASE students;
USE students;
CREATE TABLE stuinfo (
id INT PRIMARY KEY,#主键
stuName VARCHAR ( 20 ) NOT NULL UNIQUE,#非空并且唯一
gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查
seat INT UNIQUE,#唯一
age INT DEFAULT 18,#默认约束
majorId INT REFERENCES major ( id ) #外键,(没有效果)
);
CREATE TABLE major(
id INT PRIMARY KEY,
majorName VARCHAR(20)
);
# 查看stuinfo中的所有索引,包括主键、外键、唯一
SHOW INDEX FROM stuinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 添加表级约束
在各个字段的最下面
【constraint 约束名】 约束类型(字段名)
DROP TABLE IF EXISTS stuinfo;
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 ) #外键
);
2
3
4
5
6
7
8
9
10
11
12
13
14
# 通用写法
CREATE TABLE IF NOT EXISTS stuinfo (
id INT PRIMARY KEY,
stuname VARCHAR ( 20 ),
sex CHAR ( 1 ),
age INT DEFAULT 18,
seat INT UNIQUE,
majorid INT,
CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id )
);
2
3
4
5
6
7
8
9
# 修改表时添加约束
- 添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;
- 添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
id INT,
stuname VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorid INT
)
2
3
4
5
6
7
8
9
10
添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL;1添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;1添加主键
① 列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;1② 表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);1添加唯一
① 列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;1② 表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);1添加外键
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id);1
2
# 修改表时删除约束
删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;1删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT ;1删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;1删除唯一
ALTER TABLE stuinfo DROP INDEX seat;1删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;1
# 自增长列
不用手动插入值,可以自动提供序列值,默认从1开始,步长为1
auto_increment_increment
如果要更改起始值:手动插入值
如果要更改步长:更改系统变量:set auto_increment_increment=值;
- 一个表至多有一个自增长列
- 自增长列只能支持数值型
- 自增长列必须为一个key
# 创建表时设置自增长列
create table 表(
字段名 字段类型 约束 auto_increment
)
2
3
# 修改表时设置自增长列
alter table 表 modify column 字段名 字段类型 约束 auto_increment
# 删除自增长列
alter table 表 modify column 字段名 字段类型 约束
# MySQL事物
TCL:Transaction Control Language 事务控制语言
事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行。
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
- 概念:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
- 通过show engines;来查看mysql支持的存储引擎。
- 在mysql中用的最多的存储引擎有:innodb,myisam ,memory 等。其中
innodb支持事务,而myisam、memory等不支持
# 转账案例
张三丰转给郭襄500元
# 张三丰 1000
# 郭襄 1000
update 表 set 张三丰的余额=张三丰的余额-500 where name='张三丰'
# 中间发生意外,张三丰的余额少了500,而郭襄的余额并没有增加
update 表 set 郭襄的余额=郭襄的余额+500 where name='郭襄'
2
3
4
5
6
7
8
# 事务的特性(ACID)
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
# 事务的创建
# 隐式事务
事务没有明显的开启和结束的标记,比如insert、update、delete语句
# 执行完表中id列为1的那一行数据就直接被删除了
delete from 表 where id =1;
2
# 显式事务
事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用 set autocommit=0;
# 查看自动提交是否开启
SHOW VARIABLES LIKE 'autocommit';
# 查看数据库引擎
SHOW ENGINES;
2
3
4
5
# 开启事物步骤
开启事务
# 关闭自动提交 set autocommit=0; # 开启事物(可选) start transaction;1
2
3
4
5编写事务中的sql语句(select insert update delete)
可选:
savepoint 节点名;设置回滚点结束事务
commit;提交事务 # 或者 rollback;回滚事务 # 或者 rollback to 回滚点名;回滚到指定的地方1
2
3
4
5
# 并发事务
事务的并发问题是如何发生的?
多个事务 同时 操作 同一个数据库的相同数据时
并发问题都有哪些?
脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的
不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了(在一个事物中不管读多少次,读取的数据应该都一样)
幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行
如何解决并发问题
通过设置隔离级别来解决并发问题
# 事物隔离级别
√:已解决
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| read uncommitted:读未提交 | × | × | × |
| read committed:读已提交 | √ | × | × |
| repeatable read:可重复读 | √ | √ | × |
| serializable:串行化 | √ | √ | √ |
mysql`中默认 第三个隔离级别 `repeatable read
oracle`中默认第二个隔离级别 `read committed
2
查看隔离级别
select @@tx_isolation;
设置隔离级别
# 设置当前 mySQL 连接的隔离级别:
set transaction isolation level read committed;
# 设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;
2
3
4
5
# 案例
事务的使用步骤
# 开启事务 SET autocommit=0; START TRANSACTION; # 编写一组事务的语句 UPDATE account SET balance = 1000 WHERE username='张无忌'; UPDATE account SET balance = 1000 WHERE username='赵敏'; # 结束事务 ROLLBACK; # COMMIT; SELECT * FROM account;1
2
3
4
5
6
7
8
9
10
11
12事务对于delete和truncate的处理的区别
SET autocommit=0; START TRANSACTION; DELETE FROM account; ROLLBACK;1
2
3
4
5TRUNCATE 无法回滚
SET autocommit=0; START TRANSACTION; TRUNCATE TABLE account; ROLLBACK;1
2
3
4
5savepoint 的使用
SET autocommit=0; START TRANSACTION; DELETE FROM account WHERE id=25; SAVEPOINT a;#设置保存点 DELETE FROM account WHERE id=28; ROLLBACK TO a;#回滚到保存点 SELECT * FROM account;1
2
3
4
5
6
7
8
# MySQL视图
MySQL从5.0.1版本开始提供视图功能。一种虚拟存在的表,行和列的数据来自定义视图的查询中 使用的表,并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果
# 应用场景
- 多个地方用到同样的查询结果
- 该查询结果使用的sql语句较复杂
# 优点
- 简化sql语句
- 提高了sql的重用性
- 保护基表的数据,提高了安全性
# 创建视图
create view 视图名
as
查询语句;
2
3
创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
CREATE VIEW emp_v1 AS SELECT last_name, salary, email FROM employees WHERE phone_number LIKE '011%';1
2
3
4
5
6
7
8创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息
# 创建视图,查询每个部门的最高工资,筛选出高于12000的 DROP VIEW emp_v2; CREATE VIEW emp_v2 AS SELECT department_id, MAX( salary ) max_salary FROM employees GROUP BY department_id HAVING max_salary > 12000; # 根据创建的视图连接departments表查询部门信息 SELECT d.*, ev2.max_salary FROM departments d JOIN emp_v2 ev2 ON d.department_id = ev2.department_id;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20查询姓名中包含a字符的员工名、部门名和工种信息
# 查询员工名、部门名和工种信息 CREATE VIEW emp_v3 AS SELECT e.last_name, d.department_name, j.job_title FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id LEFT JOIN jobs j ON e.job_id = j.job_id; # 筛选出姓名中包含a字符的员工 SELECT * FROM emp_v3 WHERE last_name LIKE '%a%';1
2
3
4
5
6
7
8
9
10
11
12查询各部门的平均工资级别
# 查询各部门的平均工资 CREATE VIEW emp_v4 AS SELECT AVG( salary ) avg_salary, department_id FROM employees GROUP BY department_id; # 查询各部门的平均工资级别 SELECT department_id, j.grade_level FROM emp_v4 ev4 LEFT JOIN job_grades j ON ev4.avg_salary BETWEEN j.lowest_sal AND j.highest_sal1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18查询平均工资最低的部门信息
SELECT d.*, ev4.avg_salary FROM departments d JOIN ( SELECT * FROM emp_v4 ORDER BY avg_salary LIMIT 1 ) ev4 ON d.department_id = ev4.department_id1
2
3
4
5
6
7
# 修改视图
# 方式一
create or replace view 视图名
as
查询语句;
2
3
# 方式二
alter view 视图名
as
查询语句;
2
3
# 删除视图
drop view 视图名,视图名,...
DROP VIEW emp_v1,emp_v2,emp_v3,emp_v4;
2
# 查看视图
DESC 视图名;
SHOW CREATE VIEW 视图名;
CREATE VIEW emp_v1 AS SELECT
*
FROM
employees;
DESC emp_v1;
SHOW CREATE VIEW emp_v1;
2
3
4
5
6
7
8
9
10
11
# 视图的更新
视图的可更新性和视图中查询的定义有关系,以下类型的视图是不能更新的。
- 包含以下关键字的sql语句:分组函数、distinct、group by
- having、union或者union all
- 常量视图
- Select中包含子查询
- join
- from一个不能更新的视图
- where子句的子查询引用了from子句中的表
# 案例
张飞能否成功插入?
# 创建视图,查询员工的姓名,邮箱和年薪
CREATE
OR REPLACE VIEW myv1 AS SELECT
last_name,
email,
salary * 12 *(1+IFNULL ( commission_pct, 0 )) "annual salary"
FROM
employees;
# 插入一条数据
INSERT INTO myv1 VALUES('张飞','zf@qq.com',94862.00);
2
3
4
5
6
7
8
9
10
11
12
张飞能否成功插入?
# 创建视图,查询员工的姓名和邮箱
CREATE
OR REPLACE VIEW myv1 AS SELECT
last_name,
email
FROM
employees;
# 插入一条数据
INSERT INTO myv1 VALUES('张飞','zf@qq.com');
2
3
4
5
6
7
8
9
10
11
能否将张飞修改为张无忌?
UPDATE myv1 SET last_name = '张无忌' WHERE last_name='张飞';
能否干掉张无忌?
DELETE FROM myv1 WHERE last_name = '张无忌';
能否将10号部门的最高薪水改为9000?
# 创建视图,查询每个部门的最高工资
CREATE
OR REPLACE VIEW myv1 AS SELECT
MAX( salary ) m,
department_id
FROM
employees
GROUP BY
department_id;
# 将10号部门的最高薪水改为9000
UPDATE myv1 SET m=9000 WHERE department_id=10;
2
3
4
5
6
7
8
9
10
11
12
能否更改?
CREATE OR REPLACE VIEW myv2
AS
SELECT 'john' NAME;
#更新
UPDATE myv2 SET NAME='lucy';
2
3
4
5
6
能够将最高工资列修改为100000?
CREATE OR REPLACE VIEW myv3 AS
SELECT department_id,( SELECT MAX( salary ) FROM employees ) 最高工资
FROM
departments;
# 修改
UPDATE myv3 SET 最高工资=100000;
2
3
4
5
6
7
8
修改和插入能成功吗?
# 创建视图,查询员工名与部门名
CREATE
OR REPLACE VIEW myv4 AS SELECT
last_name,
department_name
FROM
employees e
JOIN departments d ON e.department_id = d.department_id;
# 修改
UPDATE myv4 SET last_name = '张飞' WHERE last_name='Whalen';
# 插入
INSERT INTO myv4 VALUES('陈真','Acc');
2
3
4
5
6
7
8
9
10
11
12
13
能修改吗?
CREATE
OR REPLACE VIEW myv5 AS SELECT
*
FROM
myv3;
# 修改
UPDATE myv5 SET 最高工资=10000 WHERE department_id=60;
2
3
4
5
6
7
8
能修改吗?
# 查询所有的领导信息
CREATE OR REPLACE VIEW myv6
AS
SELECT last_name,email,salary
FROM employees
WHERE employee_id IN(
SELECT manager_id
FROM employees
WHERE manager_id IS NOT NULL
);
# 修改
UPDATE myv6 SET salary=10000 WHERE last_name = 'k_ing';
2
3
4
5
6
7
8
9
10
11
12
13
14
查询领导Id(将所有员工的上级Id查出来,这些Id就是领导Id):
SELECT DISTINCT manager_id FROM employees WHERE manager_id IS NOT NULL
# 视图和表的对比
| 关键字 | 是否占用物理空间 | 使用 | |
|---|---|---|---|
| 视图 | view | 占用较小,只保存sql逻辑 | 一般用于查询 |
| 表 | table | 保存实际的数据 | 增删改查 |
# 变量
# 系统变量
系统变量:
- 全局变量
- 会话变量
说明:变量由系统定义,不是用户定义,属于服务器层面
注意:全局变量需要添加global关键字,会话变量需要添加session关键字,如果不写,默认会话级别
查看所有系统变量
# 全局 show global variables; #会话 show【session】variables;1
2
3
4
5查看满足条件的部分系统变量
show global |【session】 variables like '%char%';1查看指定的系统变量的值(
带.)select @@global |【@@session】.系统变量名;1为某个系统变量赋值
方式一
set global|【session】系统变量名=值;1方式二(
带.)set @@global|【@@session】.系统变量名=值;1
例如:
# 查看所有全局变量
SHOW GLOBAL VARIABLES;
# 查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';
# 查看指定的系统变量的值
SELECT @@global.autocommit;
# 为某个系统变量赋值
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;
# 查看所有会话变量
SHOW SESSION VARIABLES;
# 查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE '%char%';
# 查看指定的会话变量的值
SELECT @@autocommit;
SELECT @@session.tx_isolation;
# 为某个会话变量赋值
SET @@session.tx_isolation='read-uncommitted';
SET SESSION tx_isolation='read-committed';
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 自定义变量
变量由用户自定义,而不是系统提供的
作用域:针对于当前会话(连接)有效,作用域同于会话变量
声明并初始化
赋值操作符:=或:=
SET @变量名=值;
SET @变量名:=值;
SELECT @变量名:=值;
2
3
更新变量的值
方式一
SET @变量名=值; SET @变量名:=值; SELECT @变量名:=值;1
2
3方式二
SELECT 字段 INTO @变量名 FROM 表;1
2
查看变量的值
SELECT @变量名;
# 局部变量
作用域:仅仅在定义它的begin end块中有效
应在 begin end中的第一句话声明
声明
DECLARE
变量名 类型;
...
END
##
DECLARE
变量名 类型 【DEFAULT 值】;
...
END
2
3
4
5
6
7
8
9
10
11
赋值(更新变量的值)
方式一
SET 局部变量名=值; SET 局部变量名:=值; SELECT 局部变量名:=值;1
2
3方式二
SELECT 字段 INTO 具备变量名 FROM 表;1
2
查看变量的值
SELECT 局部变量名;
# 案例
声明两个用户变量,求和并打印
SET @m=1; SET @n=1; SET @sum=@m+@n; SELECT @sum;1
2
3
4
# 存储过程
什么是存储过程:
事先经过编译并存储在数据库中的一段sql语句的集合。
类似于java中的方法
使用好处
- 简化应用开发人员的很多工作
- 减少数据在数据库和应用服务器之间的传输
- 提高了数据处理的效率
一组预先编译好的SQL语句的集合,理解成批处理语句
# 创建语法
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
存储过程体(一组合法的SQL语句)
END
2
3
4
5
参数列表包含三部分
- 参数模式
- 参数名
- 参数类型
例:in stuname varchar(20)
参数模式:
in:该参数可以作为输入,也就是该参数需要调用方传入值
out:该参数可以作为输出,也就是该参数可以作为返回值
inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值
如果存储过程体仅仅只有一句话,begin end可以省略
存储过程体中的每条sql语句的结尾要求必须加分号。
存储过程的结尾可以使用 delimiter 重新设置(避免与存储过程体的分号混淆)
delimiter 结束标记 # 例 delimiter $1
2
3
例:
插入到admin表中五条记录
USE girls;
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username,`password`)
VALUES
('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom','0000');
END $
2
3
4
5
6
7
8
9
# 调用语法
CALL 存储过程名(实参列表);
CALL myp1();
2
# 创建带in模式参数的存储过程
创建存储过程实现 根据女神名,查询对应的男神信息
DELIMITER $
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
SELECT bo.*
FROM boys bo
RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name=beautyName;
END $
CALL myp2('赵敏');
2
3
4
5
6
7
8
9
创建存储过程实现,判断用户是否登录成功
DELIMITER $
CREATE PROCEDURE myp3(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
DECLARE result INT DEFAULT 0; #声明并初始化
SELECT COUNT(*) INTO result #赋值
FROM admin
WHERE admin.username = username
AND admin.password = PASSWORD;
SELECT IF(result>0,'成功','失败'); #使用
END $
CALL myp3('john','8888');
CALL myp3('john','1234');
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建out模式参数的存储过程
根据输入的女神名,返回对应的男神名
DELIMITER $
CREATE PROCEDURE myp4(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
SELECT bo.boyname INTO boyname
FROM boys bo
RIGHT JOIN
beauty b ON b.boyfriend_id = bo.id
WHERE b.name=beautyName ;
END $
# 调用 使用自定义变量接收
CALL myp4('赵敏',@name);
SELECT @name;
2
3
4
5
6
7
8
9
10
11
12
根据输入的女神名,返回对应的男神名和魅力值
DELIMITER $
CREATE PROCEDURE myp5(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT usercp INT)
BEGIN
SELECT boys.boyname ,boys.usercp INTO boyname,usercp
FROM boys
RIGHT JOIN
beauty b ON b.boyfriend_id = boys.id
WHERE b.name=beautyName ;
END $
CALL myp5('小昭',@name,@cp);
SELECT @name,@cp;
2
3
4
5
6
7
8
9
10
11
12
# 创建带inout模式参数的存储过程
传入a和b两个值,最终a和b都翻倍并返回
DELIMITER $
CREATE OR REPLACE PROCEDURE myp5(INOUT a INT,INOUT b INT)
BEGIN
SET a:=a*2;
SET b:=b*2;
END $
SET @m=10;
SET @n=20;
CALL myp5(@m,@n);
SELECT @m,@n;
2
3
4
5
6
7
8
9
10
11
12
# 查看存储过程的信息
SHOW CREATE PROCEDURE myp5;
# 存储函数
# 存储过程与存储函数的区别
存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新
存储函数:有且仅有1 个返回,适合做处理数据后返回一个结果
# 创建语法
CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
BEGIN
函数体
END
2
3
4
- 参数列表 包含两部分:参数名,参数类型
- 函数体:
- 肯定会有return语句,如果没有会报错 如果return语句没有放在函数体的最后也不报错,但不建议
- 函数体中仅有一句话,则可以省略begin end
- 使用 delimiter语句设置结束标记
# 调用语法
SELECT 函数名(参数列表);
# 无参有返回
返回公司的员工个数
USE myemployees;
DELIMITER //
CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0; #定义局部变量
SELECT COUNT(*) INTO c #赋值
FROM employees;
RETURN c;
END //
SELECT myf1();
2
3
4
5
6
7
8
9
10
11
12
# 有参有返回
根据员工名,返回它的工资
DELIMITER //
CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN
SET @sal=0; #定义用户变量
SELECT salary INTO @sal #赋值
FROM employees
WHERE last_name = empName;
RETURN @sal;
END //
SELECT myf2('Kochhar');
SELECT @sal;
2
3
4
5
6
7
8
9
10
11
12
13
14
根据部门名,返回该部门的平均工资
DELIMITER //
CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
DECLARE sal DOUBLE ;
SELECT AVG(salary) INTO sal
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.department_name=deptName;
RETURN sal;
END //
SELECT myf3('IT');
2
3
4
5
6
7
8
9
10
11
# 查看函数
SHOW CREATE FUNCTION myf3;
# 删除函数
DROP FUNCTION myf3;
创建函数,实现传入两个float,返回二者之和
DELIMITER //
CREATE OR REPLACE FUNCTION myf3(num1 FLOAT,num2 FLOAT) RETURNS FLOAT(3,1)
BEGIN
DECLARE SUM FLOAT(3,1) DEFAULT 0;
SET SUM=num1+num2;
RETURN SUM;
END //
SELECT myf3(15.5,15.3); --> 30.8
2
3
4
5
6
7
8
# 流程控制结构
# 分支结构
# if函数
语法:if(条件,值1,值2)
功能:实现双分支
应用在begin end中或外面
# case结构
类似java中的switch
功能:实现多分支
应用在begin end 中或外面
语法一:
case 表达式或字段
when 值1 then 语句1;
when 值2 then 语句2;
...
else 语句n;
end [case];
2
3
4
5
6
语法二:
case
when 条件1 then 语句1;
when 条件2 then 语句2;
...
else 语句n;
end [case];
2
3
4
5
6
创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
DELIMITER //
CREATE OR REPLACE FUNCTION test_case(score DOUBLE) RETURNS CHAR
BEGIN
-- DECLARE result CHAR;
CASE
WHEN score>90 THEN
RETURN 'A';
WHEN score>80 THEN
RETURN 'B';
WHEN score>60 THEN
RETURN 'C';
ELSE
RETURN 'D';
END CASE;
END //
SELECT test_case(95);
SELECT test_case(85);
SELECT test_case(75);
SELECT test_case(55);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# if结构
功能:实现多分支
只能放在begin end中
语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;
...
else 语句n;
end if;
2
3
4
5
创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
DELIMITER //
CREATE OR REPLACE FUNCTION test_if(score DOUBLE) RETURNS CHAR
BEGIN
DECLARE result CHAR;
IF score>90 THEN
SET result:='A';
ELSEIF score>80 THEN
SET result:='B';
ELSEIF score>60 THEN
SET result:='C';
ELSE
SET result:='D';
END IF;
RETURN result;
END //
SELECT test_if(95);
SELECT test_if(85);
SELECT test_if(75);
SELECT test_if(55);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
创建存储过程,如果工资<2000,则删除,如果5000>工资>2000,则涨工资1000,否则涨工资500
DELIMITER //
CREATE OR REPLACE PROCEDURE test_if2(IN salary DOUBLE)
BEGIN
IF salary<2000 THEN
DELETE FROM employees WHERE employees.salary=salary;
ELSEIF 2000<salary<5000 THEN
UPDATE employees SET employees.salary=employees.salary+1000 WHERE employees.salary=salary;
ELSE
UPDATE employees SET employees.salary=employees.salary+500 WHERE employees.salary=salary;
END IF;
END //
CALL test_if2(3300.00);
2
3
4
5
6
7
8
9
10
11
12
13
# 循环结构
分类:while、loop、repeat
循环控制:
iterate类似于 continue,继续,结束本次循环,继续下一次
leave 类似于 break,跳出,结束当前所在的循环
只能放在begin end中
标签:用上循环控制就必须要加
# while
[标签:] while 循环条件 do
循环体;
end while [标签];
2
3
批量插入,根据次数插入到admin表中多条记录
USE girls;
DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i<total DO
INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');
SET i:=i+1;
END WHILE;
END //
CALL test_while(10);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止 (需要加标签)
DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN
DECLARE i INT DEFAULT 1;
a:WHILE i<=total DO
IF i>20 THEN LEAVE a;
END IF;
INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');
SET i:=i+1;
END WHILE a;
END //
TRUNCATE TABLE admin;
CALL test_while(100);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
批量插入,根据次数插入到admin表中多条记录,只插入偶数次
DELIMITER //
CREATE OR REPLACE PROCEDURE test_while(IN total INT)
BEGIN
DECLARE i INT DEFAULT 0;
a:WHILE i<=total DO
SET i:=i+1;
IF MOD(i,2)!=0 THEN ITERATE a;
END IF;
INSERT INTO admin VALUES(NULL,CONCAT('员工',i),'8888');
END WHILE a;
END //
TRUNCATE TABLE admin;
CALL test_while(20);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# loop
[标签:] loop
循环体;
end loop [标签];
2
3
4
5
6
# repeat
[标签] repeat
循环体;
until 结束循环的条件
end repeat [标签];
2
3
4