MySQL基础
(Based on MySQL8.0 and Windows10)
注:CSDN不支持MySQL语法高亮,所以SQL语句看起来可能比较别扭。
基础命令
通过命令行对MySQL进行启停
(需要以管理员权限启动powershell或CMD)
(如果提示无法识别为cmdit,说明MySQL的bin目录没有添加到PATH(在环境变量里)路径中)
(解决方法:将MySQL的bin目录的绝对地址(C:\Program Files\MySQL\MySQL Server 8.0\bin)复制到PATH中)
启动:net start mysql80(对应的mysql服务名)
停止:net stop mysql80
通过命令行进行登录和退出
登录:mysql -h(host也就是主机名)localhost -P(端口) 3306 -u(用户) root -p(密码)
只连接本机的数据库,可以简化为mysql -u root -p
注意:如果出现提示无法识别为cmdit,那么就是没有把MySQL的bin目录添加到path中
常见命令
- 查看当前所有的数据库
show databases;
- 打开指定库
use 库名;
- 查看当前库所有表
show tables;
- 查看其他库的所有表
show tables from 库名;
- 创建表
create talbe 表名(
列名 列类型.
);
- 查看表结构
desc 表名
- 查看服务器版本
方法一:登录到mysql,select version();
方法二:在CLI中输入mysql --version或mysql --V
MySQL语法规范
- 不区分大小写,但是建议关键字大写,表名、列名小写
- 每条命令最好用“;”结尾
- 每条命令根据需要,可以进行缩进或换行
- 注释
- 单行注释:#注释文字
- 单行注释:-- 注释文字(有空格)
- 多行注释:/注释文字/
数据库字段解释
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EyfV2z9K-1611835514094)(C:\Users\SHQ\AppData\Roaming\Typora\typora-user-images\image-20201125062538201.png)]
DQL语言(查询语言)
基础查询
语法:
SELECT 查询列表
FROM 表名;
特点:
-
查询列表可以是:表中的字段、常量值、表达式、函数
-
查询的结果是一个虚拟的表格
使用:
-
查询表中的单个字段:
SELECT last_name FROM employees;
-
查询表中的多个字段
SELECT last_name,salary,email FROM employees;
-
查询表中所有字段
a. SELECT * FROM employees;/*这种方式各列顺序就是原来的样子*/ b. SELECT 'XXXX', 'XXXXXX' FROM employees;/*这种方式各列顺序按照查询关键字的排列顺序来*/
-
查询常量值(注意日期和字符必须用单引号引起来,数值型不需要)
SELECT 100;
-
查询表达式
SELECT 100*100;
-
查询函数
SELECT VERSION();
-
起别名(1.便于理解2.如果要查询的字段有重名的情况,使用别名可以区分开来)
方式一:使用AS SELECT 100%98 AS result; SELECT last_name AS 姓氏, first_name as 名 FROM employees; 方式二:使用空格 SELECT last_name 姓氏, first_name 名 FROM employees; 案例:查询salary,显示结果为out put SELECT salary AS "out put" FROM employees;
-
去重(DISTINCT应用于所有列,而不仅仅是放在它后面的那一列)
案例:查询员工表中涉及到的所有的部门编号 SELECT DISTINCT department_id from employees; #如果后面有多个列,只有这多个列全部相同的情况才会去重。否则所有的行都会被检索出来。
-
“+”号的作用(只有运算符的功能)
SELECT 100 + 90;/*两个操作数都为数值型,则做加法运算*/ SELECT '123' + 90;/*其中一方为字符型,则试图将字符型数值转换为数值型;*/ SELECT 'john' + 90; /*转换成功,则继续做加法运算; 转换失败,则将字符型数值转换成0;*/ SELECT null + 10/*null与任何值都是null*/
-
拼接查询
SELECT CONCAT('a','b','c') AS result; SELECT CONCAT(last_name,first_name) AS 姓名 FROM employees;
-
限制查询行数
# 查询表中的前五行
SELECT prod_name FROM products
LIMIT 5
# 查询从表第三行开始往后的四行
SELECT prod_name FROM products
LIMIT 3, 4
#也可以写成下面这样(从MySQL5开始)
SELECT prod_name FROM products
LIMIT 4 OFFSET 3
条件查询
语法:
SELECT 查询列表/*第三步执行*/
FROM 表名/*首先执行*/
WHERE 筛选条件/*第二步执行*/
分类:
1. 按条件表达式筛选
条件运算符:> < = != <>(不等于) >= <=
# 案例一:查询工资大于12000的
SELECT *
FROM employees
WHERE salary>12000;
# 案例二:查询部门编号不等于90号的员工名和部门号
SELECT last_name,
department_id
FROM employees
WHERE department_id <> 90
2. 按逻辑表达式筛选
逻辑运算符:&&和and:两个条件都为true,结果为true,反之为fals
or和||:只要有一个条件为true,结果为true,反之为false
#计算次序:在处理OR之前会先去处理AND操作符(也就是AND优先级高于OR)
#当出现 prod_price>=10 AND vend_id=1002 OR vend_id=1003
#理解为 (prod_price>=10 AND vend_id=1002) OR vend_id=1003
#解决方法:使用圆括号明确地进行分组(只要在WHERE中使用AND和OR就应该使用圆括号)
not和! 如果连接的条件本身为false,结果为true,反之为false
#MySQL支持not对IN、BETWEEN和EXISTS子句取反
# 案例一:查询工资在10000到20000之间的员工名、工资以及奖金
SELECT last_name,salary,ifnull(commission_pct,0)
FROM employees
WHERE salary>=1000 && salary <=20000;
# 案例二:查询部门编号不是在90到110之间,或者工资高于15000的员工信息
SELECT *
FROM employees
WHERE department_id < 90 || department_id > 110 || salary > 15000;
3. 模糊查询
运算符:
1. like:/* 一般和通配符( %:代表任意字符出现任意次数,包含0个字符;
_表示单个字符)搭配使用*/
# 注意尾空格:尾空格会干扰通配符匹配。例如:ani_ 这样在使用 %ani 时不会匹配到它
# 通配符不能匹配NULL值
# 注意事项:
# Ⅰ 不要过度使用通配符。(因为速度比较慢)如果其他操作符可以达到相同目的,应该使用其他操作符
# Ⅱ 使用通配符的时候除非确实有必要,不然不要把它们用在搜索开始处
# Ⅲ 注意通配符的位置。放错位置会导致匹配不到想要的内容
# 案例一:查询员工名中包含字符a的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '%a%';
# 案例二:查询员工名字中第三个字符为e,第五个字符为a的员工名和工资
SELECT *
FROM employees
WHERE last_name like '__e_a%';
# 案例三:查询员工名中第二个字符为_的员工
SELECT *
FROM employees
WHERE last_name like '_$_' ESCAPE '$';
2. between and # 1.包含临界值 2.两个临界值不要调换位置
#案例:查询员工编号在100到120之间的员工信息
SELECT *
FROM employees
WHERE employee_id BETWEEN 100 AND 120;
3. in # 含义:判断某字段的值是否属于in列表中的某一项
# 特点:1.使用in提高语句简洁度
# 2.in列表的值类型必须统一或兼容
# 3.功能与OR相当。但in的执行速度一般比or快
# 4.可以包含其他SELECT子句,能够更动态地建立WHERE子句
# 案例:查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编 号
SELECT last_name,job_id
FROM employees
WHERE job_id IN ('IT_PROG','AD_VP', 'AD_PRES');
4. is null # 仅可以判断NULL值,推荐使用
# 案例一:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct
FROM employees
WHERE commission_pct IS NULL;
5. <=> # 名称:安全等于
# 用的比较少
# 既可以判断NULL值,又可以判断普通数值,可读性较低
# 案例一:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct
FROM employees
WHERE commission_pct <=> NULL;
#案例二: 查询工资为12000的员工信息
SELECT last_name,commission_pct
FROM employees
WHERE salary <=> 12000;
练习
# 1. 查询员工号为176的员工的姓名和部门号和年薪
SELECT last_name, department_id, salary * 12 * (1 + IFNULL(commission_pct,0))
FROM employees
WHERE employee_id = 176;
# 2. 查询employee表中,job_id不为‘IT’或者工资为12000的员工信息
SELECT salary, last_name
FROM employees
WHERE job_id != 'IT' || salary = 12000;
# 3.查看部门departments表的结构
DESC departments;
# 查询结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zrmMkHVa-1611835514106)(C:\Users\SHQ\AppData\Roaming\Typora\typora-user-images\image-20201125081058769.png)]
# 4. 查询部门departments表中涉及到了哪些位置编号
SELECT DISTINCT location_id
FROM departments;
# 5. 经典面试题:
# 试问:select * from employee;和select * from employees where commission_pct like ‘%%’ and last_name like ‘%%’;
# 结果是否一样?并说明原因
答:不一样。如果判断的字段有NULL值,则为NULL。
排序查询
# 语法:
SELECT 查询列表
FROM 表
[WHERE 筛选条件]
ORDER BY 排序列表 [ASCIIDESC]
# 特点:
1. 默认为升序(ASC).
2. order by子句中可以支持单个字段、多个字段、表达式、函数、别名
3. order by子句一般是放在查询语句的最后边。limit子句除外
4. 任意列都可以使用(甚至非选择的列也可以使用)
# 案例1:查询员工信息,要求工资从高到低排序
SELECT *
FROM employees
ORDER BY salary DESC
# 降序(DESC)
# 案例2:查询部门编号>=90的员工信息,按入职世嘉你的先后进行排序【添加筛选条件】
SELECT *
FROM employees
WHERE department_id >= 90
ORDER BY hiredate
# 案例3:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT *,(salary*12*(1+IFNULL(commission_pct,0))) SALARY
FROM employees
ORDER BY (employees.salary*12*(1+IFNULL(commission_pct,0)))
# 案例4:按年薪的高低显示员工的信息和年薪【按别名排序】
SELECT *,(salary*12*(1+IFNULL(commission_pct,0))) SALARY
FROM employees
ORDER BY SALARY DESC
# 案例5:按姓名的长度显示员工的姓名和工资【按函数排序】
SELECT LENGTH(last_name) length,last_name,salary
FROM employees
ORDER BY LENGTH(last_name) DESC
# 案例6:查询员工信息,要求先按工资排序,再按员工编号排序【按多个字段排序】
SELECT *
FROM employees
ORDER BY salary, employee_id DESC
练习
# 1.查询员工的姓名和部门号和年薪,按年薪降序,按姓名升序
SELECT last_name,department_id, salary*12*(1+IFNULL(commission_pct,0)) SALARY
FROM employees
ORDER BY SALARY DESC, last_name
# 2.选择工资不在8000到17000的员工的姓名和工资,按工资降序
SELECT last_name, salary
FROM employees
WHERE salary NOT BETWEEN 8000 AND 17000
ORDER BY salary DESC
# 3.查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT *
FROM employees
WHERE email LIKE '%e%'
ORDER BY LENGTH(email) DESC, department_id
常见函数
# 概念:类似于java的方法,将一组逻辑语句封装在方法中,对外暴露方法名
# 好处:
# 1.隐藏了实现细节
# 2.提高了代码的重用性
# 调用:
# SELECT 函数名() 【FROM】表
# 特点:
# Ⅰ 叫什么(函数名)
# Ⅱ 干什么(函数功能)
# 分类:
# Ⅰ 单行函数
# 如:concat、length、ifnull等
# Ⅱ 分组函数
# 功能:做统计使用:又称为统计函数、聚合函数、组函数
具体使用
# length 获取参数值的字节个数
SELECT LENGTH('JOHN');
SELECT LENGTH('张三丰');
# concat 拼接字符串
SELECT CONCAT(last_name,'_',first_name) Name
FROM employees
# upper、lower 转换大小写
SELECT UPPER('john');
SELECT LOWER('JOHN')
# 示例:将姓变大写,名变小写,然后拼接
# substr、substring【SQL的字符串是从1开始的】
SELECT SUBSTR('李莫愁爱上了陆展元',5) output
#两个参数截取从指定索引处指定字符长度的字符
SELECT SUBSTR('李莫愁爱上了陆展元',1,3) output
# 案例:姓名中首字符大写,其他字符小写,然后用_拼接,显示出来。
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2)))
FROM employees;
# instr 返回子串第一次出现的索引,如果找不到返回0
SELECT instr('杨不悔爱上了殷六侠', '殷六侠')
# trim 默认情况下去掉空格,给定参数后,可以去除指定参数的字符
默认: SELECT trim(' dfdf ')
给定参数后:SELECT trim('a' from 'eeeeeedfdfaaaaaaaaaa') name
# LPAD左填充指定长度的给定字符
# RPAD右填充指定长度的给定字符
SELECT LPAD('殷素素',10,'*')
SELECT RPAD('殷素素',10,'*')
# replace 替换
SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏')
数学函数
# round 四舍五入
# 默认取整
SELECT ROUND(-1.561)
# 可以增加第二个参数,设置保留的小数位数
SELECT ROUND(-1.561,1)
# ceil向上取整【返回>=该参数的最小整数】
SELECT CEIL(1.02)
# floor向下取整【返回<=该参数的最大整数】
SELECT FLOOR(1.02)
# truncate 截断【小数点后保留第二个参数所给的位数】
SELECT TRUNCATE(1.02,1)
# mod 取余
SELECT MOD(13,3)
# rand获取随机数,返回0-1直接的小数
日期函数
# now 返回当前系统日期+时间
SELECT NOW()
# curdate 返回当前系统日期,不包含时间
SELECT CURDATE()
# curtime 返回当前时间,不包含日期
SELECT CURTIME()
#可以获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW()) year;
SELECT YEAR('1998-08-16') year;
#例:
SELECT YEAR(hiredate) year
FROM employees
SELECT MONTH(now()) month
SELECT MONTHNAME(NOW()) month # 输出月份的英文
# str_to_date:将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE('2020-5-20','%Y-%m-%d')
# datediff: 返回两个日期相差的天数
#格式符
# 查询入职日期为1992-4-3的员工信息
SELECT *
FROM employees e
WHERE e.hiredate = '1992-4-3';
# 针对从网页获取的用户输入是一个字符串的解决方案:
SELECT *
FROM employees e
WHERE e.hiredate = str_to_date('4-3 1992','%m-%d %Y');
# date_format:将日期转换成字符
SELECT DATE_FORMAT('2020-06-06','%y年%m月%d日')
# 查询有奖金的员工名和入职日期(格式:xx月/xx日 xx年)
SELECT CONCAT(last_name,'_',first_name) name, DATE_FORMAT(hiredate, '%m月/%d日 %Y')
FROM employees e
WHERE e.commission_pct IS NOT NULL;
其他函数
# 查看数据库版本
SELECT VERSION();
# 查看当前使用的数据库
SELECT DATABASE();
# 查看用户
SELECT USER();
# 返回字符的密码形式
SELECT PASSWORD('字符')
流程控制函数
# 1.if函数:实现if-else的效果
SELECT IF(10>5,'BIG','SMALL')
# 案例:查询是否有奖金
# 2. case-when函数【既可以作为表达式。也可以作为语句】
# 使用方法一:switch-case的效果(CASE-WEHN-END)
# CASE 要判断的字段或表达式
# WHEN 常量n: THEN 要显示的值或语句n;
# 案例:查询员工的工资,要求:
# 部门号=30,显示的工资为1.1倍
# 部门号=40,显示的工资为1.2倍
# 部门号=50,显示的工资为1.3呗
# 其他部门都显示原工资
SELECT salary OLD,
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 NEW
FROM employees;
# 使用方法二:类似于多重if
# case
# when 条件n then 要显示的值n或语句
# ………………………………………………………………
# else 要显示的值n或语句n
# 案例:查询员工的工资情况
# 如果工资>=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 LEVELS
FROM employees
练习
# 1.显示系统时间
select now()
# 2.查询员工号、姓名、工资,以及工资提高20%以后的结果
select job_id,last_name,salary old, salary*1.2 new
from employees
# 3.将员工的姓名按照首字母排序,并且写出姓名的长度
select last_name, length(last_name) length
from employees e
order by substr(last_name,1,1)
# 4.做一个查询,产生下面的结果
# <last_name> earns <salary> monthly but wants <salary*3>
select concat(last_name,' earns ', salary, ' monthly but wants ', salary*3) dream_salary
from employees;
# 5. 使用case-when,按照下面的条件:
# job grade
# AD_PRES A
# ST_MAN B
# IT_PROG C
# SA_REP D
# ST_CLEAR E
# 产生下面的结果:
# last_name job_id Grade
# king AD_PRES A
select last_name,
job_id job,
case job_id
when 'AD_PRES' then 'A'
when 'ST_MAN' then 'B'
when 'IT_PROG' then 'C'
when 'SA_REP' then 'D'
when 'SH_CLERK' then 'E'
end Grade
from employees
where job_id = 'AD_PRES
分组函数
# 功能:用作统计使用,又称为集合函数或统计函数或组函数。
# 分类:
# sum 求和, avg 平均值, max 最大值, min 最小值, count 计算个数
# 特点:
# Ⅰ SUM、AVG一般用于处理数值型
# Ⅱ MAX、MiN、COUNT可以处理任何类型
# Ⅲ 以上分组都忽略NULL
# Ⅳ 和distinct搭配实现去重运算
# Ⅴ 和分组函数一同查询的字段要求是group by后的字段
# 1. 简单使用:
SELECT SUM(salary)
FROM employees
SELECT AVG(salary)
FROM employees
SELECT MAX(salary)
FROM employees
SELECT MIN(salary)
FROM employees
SELECT COUNT(salary)
FROM employees
SELECT ROUND(AVG(salary))
FROM employees
# 搭配DISTINCT
SELECT SUM(salary),SUM(DISTINCT salary), AVG(commission_pct)
FROM employees
SELECT COUNT(DISTINCT salary), COUNT(salary)
FROM employees
# COUNT函数的详细介绍
SELECT COUNT(salary)
FROM employees
# 用于统计所有行数
SELECT COUNT(*)
FROM employees
# 相当于多了一列常量1,有多少行,就有多少个1、与*号作用相同
SELECT COUNT(1)
FROM employees
# 效率:
# MYISAM存储引擎下,count(*)的效率高
# INNODB存储引擎下,COUNT(1)与COUNT(*)效率差不多
# 和分组函数一同查询的字段有限制
# 案例:
# 1.查询公司员工工资的最大值、最小值、平均值、总和
SELECT MAX(salary), MIN(salary), AVG(salary), SUM(salary)
FROM employees;
# 2.查询员工表中的最大入职时间和最小入职时间的相差天数
# 用到了DATEDIFF函数,使用第一个参数减去第二个参数
SELECT DATEDIFF(MAX(DISTINCT hiredate),MIN(DISTINCT hiredate))
FROM employees
# 3.查询部门编号为90的员工个数
SELECT COUNT(*)
FROM employees
WHERE department_id = 90
分组查询
# GROUP BY 子句语法
# 可以使用GROUP BY子句将表中的数据粉尘若干组
# 语法结构:
SELECT column(要求出现在GROUP BY的后面), 分组函数()
FROM 表名
[where 筛选条件]
[GROUP BY 分组条件]
[ORDER BY column]
# 注意:
# 查询列表必须特殊,要求是分组函数和GROUP BY后面出现的字段
#
# 特点:
# 1. 分组查询中的筛选条件分两类
# Ⅰ分组前筛选:数据源、原始表 位置:GROUP BY子句的前面 关键字:WHERE
# Ⅱ分组后筛选:分组后的结果集 位置:GROUP BY子句的后面 关键字:HAVING
# 2. 分组函数做条件肯定是放在HAVING子句中
# 3. 能分组前筛选的,就优先考虑使用分组前筛选
# 4. GROUP BY子句支持单个字段分组和多个字段分组(多个字段之间用逗号隔开,没有顺序要求),表达式或函数用的相对较少
# 5. 也可以添加排序(排序放在整个分组查询的最后)
# 简单的分组查询
# 引入:查询每个部门的平均工资
SELECT department_id ,AVG(salary)
FROM employees
GROUP BY department_id
# 案例1:查询每个工种的最高工资
SELECT job_id, MAX(salary)
FROM employees
GROUP BY job_id
# 案例2:查询每个位置的部门个数
SELECT location_id, COUNT(*)
FROM departments
GROUP BY location_id
# 添加筛选条件(分组前的筛选)
# 案例1:查询邮箱中包含字符a的,每个部门的平均工资
SELECT AVG(salary), department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id
# 案例2:查询有奖金的每个领导手下员工的最高工资
SELECT MAX(salary), manager_id
FROM employees
WHERE commission_pct IS NOT NULL
# 添加复杂的筛选条件(分组后的筛选,用Having)
# 案例1:
# 查询哪个部门的员工数量大于2
# 解决方案:拆分成小项
# Ⅰ 查询每个部门的员工个数
# Ⅱ 根据Ⅰ的结果进行筛选,查询那个部门的员工个数>2
SELECT department_id,COUNT(*)
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 2
# 案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT job_id, MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000
# 案例3:
# 查询领导编号大于102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
# Ⅰ 先找出编号大于102的领导手下所有的最低工资
# Ⅱ 再从查询后的表中筛选最低工资大于5000的
SELECT manager_id, MIN(salary)
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING MIN(salary) > 5000
# 按表达式或分组函数
# 案例:
# 按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
SELECT COUNT(*), LENGTH(last_name)
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*) > 5
# 按多个字段分组
# 案例:
# 查询每个部门每个工种的员工的平均工资
SELECT department_id,job_id,AVG(salary)
FROM employees
GROUP BY department_id, job_id
# 添加排序
# 案例:查询每个部门每个工种的员工的平均工资,并且按照平均工资的高低显示
SELECT department_id,job_id,AVG(salary)
FROM employees
GROUP BY department_id, job_id
ORDER BY AVG(salary)
练习
# 1.查询各个员工编号的员工工资的最大值、最小值、平均值、总和,并按员工编号升序排列
SELECT job_id ,MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees
GROUP BY job_id
ORDER BY job_id
# 2.查询员工最高工资和最低工资的差距
SELECT MAX(salary)-MIN(salary) Difference
FROM employees
# 3.查询各管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT manager_id,MIN(salary)
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary) >= 6000
# 4.查询所有部门的编号,员工数量和工资平均值,并按工资降序
SELECT department_id,COUNT(*),AVG(salary)
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC
# 5. 选择具有各个员工编号的员工人数
SELECT COUNT(*),job_id
FROM employees
GROUP BY job_id
连接查询——笛卡尔积
等值查询
# 1.含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询。
# 2.笛卡尔乘积现象:表1有 m 行, 表2有 n 行,结果 = m*n 行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件
# 3. 分类
# 按年代分类:
SQL92标准
SQL99标准【推荐】:支持内连接 + 外连接(左外和右外)+ 交叉连接
# 按功能分类:
# 内连接:(交集)
等值连接
非等值连接
自连接
# 外连接:
左外连接(主表+主表与副表的交集)
右外连接(主表+主表与副表的交集)
全外连接
# 交叉连接
# 一、SQL92标准
#1. 等值连接
/*
Ⅰ 多表等值连接的结果为多表的交集部分
Ⅱ n表连接,至少需要n-1个连接条件
Ⅲ 多表的顺序没有要求
Ⅳ 一般需要为表起别名
Ⅴ 可以搭配前面介绍的所有子句使用。
*/
# 案例1:查询女神名和对应的男神名
SELECT name, boyName
FROM boys,beauty
WHERE beauty.boyfriend_id =boys.id
# 案例2:查询员工名和对应的部门名
SELECT last_name, department_name
FROM employees,departments
WHERE employees.department_id = departments.department_id
# 2.为表起别名
/*
Ⅰ提高语句简洁度
Ⅱ 区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定
*/
# 查询员工名、工种号、工种名
SELECT last_name, j.job_id, job_title
FROM employees e, jobs j
WHERE e.job_id = j.job_id
# 3.两个表的顺序可以调换
# 查询员工名、工种号、工种名
SELECT last_name, j.job_id, job_title
FROM jobs j,employees e
WHERE e.job_id = j.job_id
# 4.可以添加筛选
# 案例1:查询有奖金的员工名、部门名
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
# 案例2:查询城市名中第二字符为o的部门名和城市名
SELECT department_name, city
FROM departments d, locations l
WHERE d.location_id = l.location_id
AND city like '_o%'
# 5.可以加分组
# 案例1:查询每个城市的部门个数
SELECT COUNT(*) NUM, city
FROM locations l,departments d
WHERE d.location_id = l.location_id
GROUP BY city
# 案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT department_name, e.manager_id, MIN(salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
AND e.commission_pct IS NOT NULL
GROUP BY department_name, d.manager_id
# 6.可以加排序
# 案例:查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT job_title, COUNT(*) NUM
FROM jobs j, employees e
WHERE j.job_id = e.job_id
GROUP BY job_title
ORDER BY NUM DESC
# 7.可以实现三表连接
# 案例:查询员工名、部门名和所在的城市
SELECT DISTINCT last_name, department_name, city
FROM employees e,departments d,locations l
WHERE e.department_id = d.department_id
AND d.location_id= l.location_id
非等值连接
# 案例1:查询员工的工资和工资级别
SELECT salary,grade_level
FROM employees e, job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal
AND j.grade_level = 'A'
自连接查询(把原本的一张表当做多张表来使用)
# 案例:查询 员工名和上级的名称
SELECT e.manager_id,e.employee_id, e.last_name, m.manager_id, m.last_name
FROM employees e, employees m
WHERE m.manager_id = e.employee_id
练习
1.、显示员工表的最大工资,工资平均值
SELECT MAX(salary), AVG(salary)
FROM employees
2.、查询员工表的员工编号、工种、姓名,按部门编号降序,工资升序
SELECT employee_id,job_id,last_name
FROM employees
ORDER BY department_id DESC , salary
3.、查询员工表的job_id中包含a和e的,并且a在e的前面
SELECT job_id
FROM employees
WHERE job_id LIKE '%a%e%'
4.、已知表student,里面有id(学号),name,gradeId(年级编号)
已知表grade,里面有id(年级编号),name(年级名)
已知表result,里面有id,score,studentNo(学号)
要求查询姓名、年级名、成绩
SELECT s.name, g.name, r.grade
FROM student s, grade g, result r
WHERE s.gradeId = g.id AND r.studentNo = s.id;
5、显示当前日期,以及去前后空格,截取子字符串的函数
SELECT NOW();
SELECT TRIM(' D ');
SELECT SUBSTR('FDFASDFD', 2, 4);
6. 显示所有员工的姓名、部门号、部门名称
SELECT e.last_name, d.department_id,d.department_name
FROM employees e, departments d
where d.department_id = e.department_id
7. 查询90号部门员工的工种和90号部门的locatoin_id
SELECT e.job_id, d.location_id
FROM employees e,departments d
WHERE e.department_id = d.department_id
AND e.department_id = 90
8. 查询所有有奖金员工的名字、部门名称、部门位置编号和所在城市
SELECT e.last_name, d.department_name, l.location_id,l.city
FROM employees e, departments d, locations l
WHERE e.commission_pct IS NOT NULL
AND e.department_id = d.department_id
AND d.location_id = l.location_id
9. 选择city在Toronto工作的员工的姓名、工种、部门编号、部门名称
SELECT e.last_name, e.job_id, d.department_id, d.department_name
FROM employees e,
departments d,
locations l
WHERE e.department_id = d.department_id
AND l.location_id = d.department_id
AND l.city = 'Toronto'
10. 查询每个工种、每个部门的部门名、工种名、和最低工资
SELECT department_name, job_title, MIN(salary)
FROM employees e,
jobs j,
departments d
WHERE j.job_id = e.job_id
AND d.department_id = e.department_id
GROUP BY job_title,department_name
11. 查询每个国家下的部门个数大于2的国家编号
SELECT country_id, COUNT(*) 部门个数
FROM locations l,
departments d
WHERE d.location_id = l.location_id
GROUP BY country_id
HAVING COUNT(*) > 2
12. 选择指定员工的姓名、员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
employees Emp manager Mgr
SELECT e.last_name employees, e.employee_id 'Emp', m.last_name Mname, m.manager_id 'Mgr#'
FROM employees e, employees m
WHERE e.manager_id = m.employee_id
AND e.last_name = 'kochhar'
外连接查询
# 应用场景:用于查询一个表中有,另一个表没有的记录
# 特点:
# 1. 外连接的查询结果为主表中的所有记录
# 如果从表中有和它匹配的,则显示匹配的值
# 如果从表中没有和它匹配的,则显示null
# 外连接查询结果=内连接结果+主表中有而从表没有的记录
#
# 2. 左外连接:left join左边的是主表
# 右外连接: right join右边的是主表
#
# 3. 左外和右外交换两个表的顺序,可以实现同样的效果
#
# 4. 全外连接(MySQL不支持)=内连接的结果+表一中有但表二中没有的+表二中有的但表一中没有的
#
# 5. 交叉连接: 笛卡尔积
# 引入:查询男朋友 不在男神表的女神名
SELECT name
FROM beauty
LEFT JOIN boys ON boyfriend_id = boys.id
WHERE boys.id IS null
# 案例1:查询哪个部门没有员工
SELECT d.*, employee_id
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.employee_id IS NULL
多表连接练习
# 1. 查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,则用null填充
SELECT b.id, b.name,bo.*
FROM beauty b
LEFT JOIN boys bo ON boyfriend_id = bo.id
WHERE b.id>3
# 2. 查询哪个城市没有部门
SELECT city
FROM locations
LEFT OUTER JOIN departments d ON locations.location_id = d.location_id
WHERE d.department_id IS NULL
# 3. 查询部门名为SAL或IT的员工信息
SELECT department_name, e.*
FROM departments
LEFT JOIN employees e ON departments.department_id = e.department_id
WHERE department_name IN ('IT', 'SAL')
ORDER BY first_name
子查询
# 含义:出现在其他语句中的select语句,称为子查询或内查询
# 外部的查询语句,称为主查询或外查询
# 特点:子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
# 分类:
# 按子查询出现的位置:
# select 后面(仅仅支持标量子查询)
# from 后面 (支持表子查询)
# where/having 后面(支持标量子查询、列子查询、行子查询)
# exists 后面(相关子查询):(支持表子查询)
#
# 按结果集的行列数不同:
# 标量子查询(结果集只有一行一列)
# 列子查询(结果集只有一行多列)
# 行子查询(结果集有多行多列)
# 表子查询(结果集一般为多行多列)
where或having后面的标量子查询
1. 标量子查询(一行一列)
2. 列子查询(多行子查询)
3. 行子查询(多列多行)
# 标量子查询特点:
Ⅰ 子查询放在小括号内
Ⅱ 子查询一般放在条件的右侧
Ⅲ 标量子查询,一般搭配着单行操作符使用(条件运算符)
# 非法使用标量子查询:查询结果不是一行一列
# 列子查询的特点:
Ⅰ 一般搭配着多行操作符使用(in、any/some、all
案例
1. 谁的工资比Abel高
Ⅰ 查询Abel的工资
SELECT salary
FROM employees
WHERE last_name = 'Abel'
Ⅱ 查询员工信息,满足salary > Ⅰ中的结果
SELECT last_name, salary
FROM employees
WHERE salary > (SELECT salary
FROM employees
WHERE last_name = 'Abel')
2. 返回job_id与141号员工相同,salary比143号员工多的员工的姓名、job_id、工资
Ⅰ 查询141号员工的job_id
SELECT job_id
FROM employees
WHERE employee_id = 14
Ⅱ 查询143号员工的salary
SELECT salary
FROM employees
WHERE employee_id = 141
Ⅲ 查询员工的姓名、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)
3. 返回公司工资最少的员工的last_name,job_id,和salary
Ⅰ 查询公司的最低工资
SELECT MIN(salary)
FROM employees
Ⅱ 查询last_name, job_id和slaary,要求salary=Ⅰ中的结果
SELECT last_name, job_id, salary
FROM employees
WHERE salary = (SELECT MIN(salary)
FROM employees)
4. 查询最低工资大于50号的部门,工资最低的部门id和最低工资
Ⅰ查询50号部门的最低工资
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
Ⅱ 查询每个部门的最低工资
SELECT MIN(salary)
FROM employees
GROUP BY department_id
Ⅲ 执行最终查询
SELECT department_id, MIN(salary) min
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (SELECT MIN(salary)
FROM employees
WHERE department_id = 50)
多行子查询
1. 返回多行
2. 使用多行比较操作符
# 操作符:
IN / NOT IN: 等于列表中的任意一个
ANY / SOME:和子查询返回的某一个值比较
ALL:和子查询返回的所有值比较
练习
# 1. 返回locaion_id是1400或1700的部门中的所有员工姓名
SELECT CONCAT(last_name, ' ', first_name) Name
FROM employees
WHERE department_id IN (SELECT distinct department_id
FROM departments
WHERE location_id IN (1400, 1700))
# 2. 返回其他部门中比job_id为‘IT_PROG’部门任一工资低的员工的:工号、姓名、job_id以及salary
SELECT employee_id, CONCAT(last_name, ' ', first_name), job_id, salary
FROM employees
WHERE salary < ANY (SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG')
行子查询(结果集一行多列或多行多列)
1. WHERE 后面
查询员工编号最小并且工资最高的员工信息
SELECT *
FROM employees
WHERE (employee_id, salary) = (SELECT MIN(employee_id), MAX(salary)
FROM employees)
2. SELECT 后面
查询每个部门的员工个数
SELECT department_id,
department_name,
(SELECT COUNT(*)
FROM employees e
where e.department_id = d.department_id) sum
FROM departments d
查询员工号为102的部门名
SELECT (SELECT d.department_name
FROM departments d
INNER JOIN employees e
on d.department_id = e.department_id
where e.employee_id = 102) name
3. FROM 后面
/*
* 将子查询结果充当一张表,要求必须起别名
*/
查询每个部门的平均工资的工资等级
SELECT e.*, g.grade_level
FROM (SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id) e
inner join (select *
from job_grades) g
on e.ag between lowest_sal and highest_sal
4. EXISTS后面(相关子查询)
表示查询结果是否为空
# 语法
# exists(完整的查询语句)
# 结果:1 或 0
Ⅰ 查询有员工的部门名
SELECT department_name
FROM departments
WHERE EXISTS(SELECT *
FROM employees
WHERE employees.department_id = departments.department_id)
Ⅱ 查询没有女朋友的男神信息
SELECT *
FROM boys
WHERE NOT EXISTS(
SELECT boyfriend_id
FROM beauty
WHERE boyfriend_id = boys.id)
Ⅲ 查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name, salary
FROM employees
WHERE department_id = (
SELECT department_id
FROM employees
WHERE last_name = 'Zlotkey')
练习
1. 查询工资比公司平均工资高的员工的员工号、姓名、工资
SELECT employee_id,last_name,salary
FROM employees
WHERE salary>(SELECT AVG(salary)
FROM employees)
ORDER BY salary
2. 查询各部门中工资比本部门平均工资高的员工的员工号、姓名和工资
SELECT employee_id, last_name, salary, d.department_id
FROM employees
INNER JOIN (SELECT AVG(salary) a, department_id
FROM employees
GROUP BY department_id) d
ON d.department_id = employees.department_id
WHERE salary>a
ORDER BY salary
3. 查询与姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id, department_id, last_name
FROM employees
WHERE department_id IN (SELECT department_id
FROM employees
WHERE last_name LIKE '%u%')
ORDER BY department_id
4. 查询在部门的location_id为1700的部门工作的员工的员工号
SELECT employee_id, last_name
FROM employees
WHERE department_id IN (SELECT department_id
FROM departments
WHERE location_id = 1700)
6. 查询管理者是King的员工姓名和工资
SELECT last_name, salary
FROM employees
WHERE manager_id IN (SELECT employee_id
FROM employees
WHERE last_name = 'K_ing')
ORDER BY salary
7. 查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为 姓.名
SELECT CONCAT(last_name,' ',first_name) name
from employees
where salary = (SELECT MAX(salary)
FROM employees)
分页查询
1. 应用场景:
当要显示的数据一页显示不全需要分页提交SQL请求
语法:
SELECT 查询列表
FROM 表
【join type】join 表2
ON 连接条件
WHERE 筛选条件
GROUP BY 分组字段
HAVING 分组后的筛选
ORDER BY 排序的字段
LIMIT offset, size
# offset:要显示条目的起始索引(起始索引从0开始)
# size:要显示的条目个数
练习
1. 查询前五条员工信息
SELECT *
FROM employees
LIMIT 5
2. 查询第十一条到第二十五条
SELECT *
FROM employees
LIMIT 10, 15
3. 查询有奖金的员工信息,并且把工资较高的前十名显示出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 10
# 特点:
# Ⅰ LIMIT语句放在查询语句最后
# Ⅱ 公式:
# 要显示的页数 page,每页的条目数size
# SELECT 查询类表
# FROM 表
# LIMIT (page-1)*size, size
子查询练习
1. 查询工资最低的员工的信息:last_name, salary
SELECT last_name, salary
FROM employees
WHERE salary = (SELECT MIN(salary)
FROM employees)
2. 查询平均工资最低的部门信息
SELECT *
FROM departments
WHERE department_id = (
SELECT department_id
FROM employee
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1
)
3. 查询平均工资最低的部门信息和该部门的平均工资
SELECT d.*, a
FROM departments d
INNER JOIN (
SELECT AVG(salary) a,department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1
) s
WHERE d.department_id = s.department_id
GROUP BY d.department_id
4. 查询平均工资最高的job信息
# 方式一:
SELECT *
FROM jobs
INNER JOIN (SELECT job_id
FROM employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1
) tab
ON tab.job_id = jobs.job_id
#方式二:
SELECT *
FROM jobs
WHERE job_id=(SELECT job_id
FROM employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1)
5.查询平均工资高于公司平均工资的部门有哪些?
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) > (SELECT AVG(salary) AG
FROM employees)
6. 查询出公司中所有的manager的详细信息
# 方式一:
SELECT DISTINCT e.*
FROM employees e
INNER JOIN employees s
ON e.employee_id = S.manager_id;
# 方式二:
SELECT *
FROM employees
WHERE employee_id IN (SELECT DISTINCT manager_id
FROM employees);
7. 各个部门中最高工资中最低的那个部门的最低工资是多少
SELECT MIN(salary), department_id
FROM employees
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY MAX(salary)
LIMIT 1)
8. 查询平均工资最高的部门的manager的详细信息:last_name, department_id, email, salary SELECT last_name, E.department_id, email, salary
FROM employees E
INNER JOIN departments d ON E.employee_id = d.department_id
WHERE E.department_id = (SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC
LIMIT 1)
联合查询
UNION 联合 合并:将多条查询语句的结果合并成一个结果
/**
* 语法:
* 查询语句1
* union
* 查询语句2
* union
* .....
*/
# 使用场景:
# 要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时
# 特点:
1. 要求多条查询语句的查询列数是一致的
2. 要求多条查询语句的查询结果中,每一列的类型和顺序最好一致
3. UNION关键字默认去重, UNION ALL可以包含重复项
# 引入:查询部门编号>90或邮箱包含a的员工信息
SELECT *
FROM employees
WHERE email LIKE '%a%'
UNION
SELECT *
FROM employees
WHERE department_id > 90
DDL语言
插入语句
# 语法:
INSERT INTO 表名(列名,.....) VALUES(值1,......)
#方式一:
# 要求:
1. 插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty(id, name, sex, borndate, phone, photo, boyfriend_id)
VALUES (13, '唐艺昕', '女', '1999-04-03', '198888888', NULL, 2)
2. 不可以为NULL的列必须插入值。可以为NULL的列如何插入值?
# 方式一:
和上边的一样
# 方式二:
INSERT INTO beauty(id, name, sex, borndate, phone)
VALUES (14, '娜扎', '女', '1999-04-03', 2)
3. 列的顺序可以调换,但是值要一一对应
INSERT INTO beauty(name,id, sex, borndate, phone)
VALUES ( '王语嫣',15, '女', '1999-04-03', 2)
4. 列数和值的个数必须一致
5. 可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致
INSERT INTO beauty
VALUES (18, '张飞', '男', NULL, '119', NULL, NULL)
# 方式二:
INSERT INTO 表名
SET 列名 = 值,列名 = 值
#例:
INSERT INTO beauty
SET id=19,
name = 'JACK',
borndate = '1998-08-14',
phone = '999',
boyfriend_id = NULL
# 两种方式大PK
1. 方式一支持插入多行,方式二不支持
INSERT INTO beauty
VALUES (18, '张飞', '男', NULL, '119', NULL, NULL),
VALUES (13, '唐艺昕', '女', '1999-04-03', '198888888', NULL, 2)
2. 方式一支持子查询,方式二不支持
INSERT INTO beauty(id,name,phone)
SELECT 26,'宋茜','11806644'
INSERT INTO beauty(id, name, phone)
SELECT id, boyName, '11809866'
from boys
where id < 3
修改记录
修改单表的记录
1. 语法:
UPDATE 表名
SET 列 = 新值,列 = 新值……
WHERE 筛选条件
练习
1. 修改beauty表中姓唐的女神的电话为13899888899
UPDATE beauty
SET phone = 13899888899
WHERE name LIKE '唐%'
2. 修改boys表中id为2的名称为张飞,魅力值为10
UPDATE boys
SET boyName = '张飞',
userCP = '10'
WHERE id = 2
修改多表的记录(级联查询)
1. 语法:
UPDATE 表1 别名
INNER | LEFT | RIGHT JOIN 表2 别名…………
ON 连接条件
SET 列 = 值,………………
WHERE 筛选条件
练习
1. 修改张无忌的女朋友的手机号为114
UPDATE boys
LEFT JOIN beauty
ON boys.id = boyfriend_id
SET beauty.phone = 114
WHERE boyName = '张无忌'
2. 修改没有男朋友的女神的男朋友编号都为2号
UPDATE beauty
LEFT JOIN boys
ON boyfriend_id = boys.id
SET boyfriend_id = 2
WHERE boys.id IS NULL
删除语句
单表删除
方式一: DELETE
语法:
DELETE FROM 表名
WHERE 筛选条件
方式二: truncate
语法:
truncate table 表名
练习
1. 删除手机号以9结尾的女神信息
DELETE FROM beauty
WHERE phone LIKE '%9'
多表删除(级联删除)
1. 语法:
DELETE 要删除的表的别名
FROM 表1 别名
INNER | LEFT | RIGHT JOIN 表2 别名…………
ON 连接条件
WHERE 筛选条件
练习
1. 删除张无忌的女朋友的信息
DELETE beauty
FROM beauty
INNER JOIN boys
ON boyfriend_id = boys.id
WHERE boyName = '张无忌'
2. 删除黄晓明的信息以及他女朋友的信息
DELETE beauty,boys
FROM beauty
INNER JOIN boys
ON boys.id = boyfriend_id
WHERE boyName = '黄晓明'
使用TRUNCATE语句(清空数据)
TRUNCATE语句中不允许有WHERE
TRUNCATE TABLE boys;
总结
1. DELETE 可以加 WHERE 条件, TRUNCATE 不能加。
2. TRUNCATE 删除,效率高一些。
3. 假如要删除的表中有自增长列:
使用 DELETE 删除后,在插入数据,自增长列的值从断点开始。
使用 TRUNCATE 删除后,自增长列的值从1开始。
4. TRUNCATE 删除没有返回值, DELETE 删除有返回值。
5. TRUNCATE 删除不能回滚, DELETE 删除可以回滚。
DDL语言(数据定义语言)
库和表的管理
库的管理
1. 库的创建
语法:
CREATE DATABASE [IF NOT EXISTS]库名;
2. 库的修改
更改库的字符集
ALTER DATABASE books CHARACTER SET
3. 库的删除
DROP DATABASE IF EXISTS books
表的管理
1. 表的创建
CREATE TABLE 表名(
列名 列的类型【(长度) 约束】,
列名 列的类型【(长度) 约束】,
列名 列的类型【(长度) 约束】,
……………………………………………………………………
列名 列的类型【(长度) 约束】
)
2. 表的修改
ALTER TABLE 表名 ADD | DROP | MODIFY | CHANGE COLUMN 列名【列类型 约束】
Ⅰ 修改列名
ALTER TABLE books CHANGE COLUMN publishDate pbuDate DATETIME
Ⅱ 修改列的类型或约束
ALTER TABLE books MODIFY COLUMN pbuDate TIMESTAMP
Ⅲ 添加新列
ALTER TABLE author ADD COLUMN annual DOUBLE
Ⅳ 删除列
ALTER TABLE author DROP COLUMN annual
Ⅴ 修改表名
ALTER TABLE author RENAME TO emp5
3. 表的删除
DROP TABLE IF EXISTS book_author;
通用的写法:
DROP DATABASE IF EXISTS 旧库名;
CREATE DATABASE 新库名;
DROP TABLE IF EXISTS 旧表名;
CREATE TABLE 表名();
4. 表的复制
Ⅰ 仅仅复制表的结构(所有结构)
CREATE TABLE copy LIKE author
Ⅱ 复制表的结构+数据
CREATE TABLE copy2
SELECT *
FROM author
只复制部分数据
CREATE TABLE copy3
SELECT id, au_name
FROM author
WHERE nation = '中国'
Ⅲ 仅仅复制某些字段
CREATE TABLE copy4
SELECT id, au_name
FROM author
WHERE 0
数据类型介绍
常见的数据类型
1. 数值型:
# 分类:
整型:
类型 字节 范围
Tinyint 1 有符号: -128 ~ 127 无符号: 0 ~ 255
Smallint 2 有符号: -32768~ 32767 无符号: 0 ~ 65535
Mediumint 3 有符号: -8388608 ~ 8388607 无符号: 0 ~ 2^12
Int\Integer 4 有符号: -2^31 ~ 2^31 无符号: 0 ~ 2^32
Bigint 8 有符号: -2^63 ~ 2^63 无符号: 0 ~ 2^64
# 如何设置无符号和有符号
int unsigned
# 特点:
# Ⅰ 如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
# Ⅱ 如果插入的数值超出了整形的范围,会报out of range异常,并且插入临界值
# Ⅲ 如果不设置长度,会有默认的长度
长度代表了显示的最大宽度,如果不够会用0在左边填充,但是必须搭配 zerofill 使用
# 分类:
小数:
定点数:
类型 字节 范围
DEC(M, D) M + 2 最大取值范围与 double 相同,给定decimal的有效范围由M和D决定
DECIMAL(M, D) M + 2 同上
浮点数:
类型 字节
float 4
double 8
# 特点:
# Ⅰ M:整数部位 + 小数部位
# D:小数部位
# M和D都可以省略
# 如果是DECIMAL,则M默认为10,D默认为0
# 如果是FLOAT和DOUBLE,则会根据插入数值的精度来决定精度
# 原则:
# 所选择的类型越简单越好,能保存的数值类型越小越好
2. 字符型:
Ⅰ 较短的文本:
类型 最多字符数 描述及存储需求
char M M为 0 ~ 255之间的整数
varchar M M为 0 ~ 65535之间的整数
# 特点:
# 写法 M的意思 特点 空间的耗费 效率
# char char(M) 最大的字符数,可以省略,默认为1 固定长度的字符 比较耗费 高
# varchar varchar(M) 最大的字符数,不可以省略 可变长度的字符 比较节省 低
binary varbinary
说明:
类似于 char 和 varchar, 不同的是它们包含二进制字符串而不包含非二进制字符串
ENUM(枚举),一次只能从中选择一个插入
# 用法:
CREATE TABLE tab_char
(
c1 enum ('a','b','c')
)
SET(集合),允许一次插入多个
# 用法:
CREATE TABLE tab_set
(
c1 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')
Ⅱ 较长的文本: text、 blob(较长的二进制数据)
3. 日期型
# 分类:
日期和时间类型 字节 最小值 最大值
date 4 1000-01-01 9999-12-31
datetime 8 1000-01-01 00:00:00 9999-12-31 23:59:59
timestamp 4 19700101080001 2038年的某个时刻
time 3 -838:59:59 838:59:59
year 1 1901 2155
# 总结:
# date 只保存日期
# time 只保存时间
# year 只保存年
# datetime 保存日期 + 时间
# timestamp 保存日期 + 时间
# 特点:
# datetime 不受时区影响
# timestamp 受时区影响
常见约束
含义:
一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性
分类:六大约束
NOT NULL:非空,用于保证该字段的值不能为空。(比如:姓名、学号等)
DEFAULT:默认,用于保证该字段有默认值(比如:性别)
PRIMARY KEY:主键,用于保证该字段的值具有唯一性(比如:学号、员工编号等)
UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空(比如:座位号)
CHECK:检查约束(MySQL8.0.16以下不支持)(比如:年龄、性别)
FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值。
在从表添加外键约束,用于引用主表中某列的值
比如:学生表的专业编号、员工表的部门编号、员工表的工种编号
# 添加约束的时机:
1. 创建表时
2. 修改表时
# 约束的添加分类:
1. 列级约束:
六大约束语法上都支持,但外键约束没有效果
2. 表级约束
除了非空、默认,其他的都支持
# 用法
CREATE TABLE 表名(
字段名 字段类型 列级约束,
字段名 字段类型,
表级约束
)
主键与唯一键的对比
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合
主键 ✔ ✖ 至多有1个 ✔,不推荐使用
唯一 ✔ ✖ 可以有多个 ✔,不推荐使用
# 外键:
# 1. 要求在从表设置外键关系
# 2. 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
# 3. 主表的关联列必须是一个key(一般是主键或唯一键)
# 4. 插入数据时,先插入主表,再插入从表
# 5. 删除数据时,先删除从表,再删除主表
创建表时添加约束
1. 添加列级约束
CREATE DATABASE students;
USE students;
CREATE TABLE stuinfo(
id INT PRIMARY KEY ,
stuName VARCHAR(20) NOT NULL ,
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)
)
# 总结:
# 语法:
# 直接在字段名和类型后面追加约束类型即可。
# 只支持:默认、非空、主键、唯一
2. 添加表级约束
# 语法:
# 在各个字段的最下面
# CONSTRAINT 约束名【可选】 约束类型(字段名)
#通用的写法:
# CREATE TABLE stuinfo
# (
# id INT PRIMARY KEY,
# stuName VARCHAR(20) NOT NULL,
# gender CHAR(1) CHECK (gender = '男' or gender = '女'),
# seat INT,
# age INT,
# majorId INT,
#
# CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorId) references major (id)#外键
# )
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)#外键
)
修改表时添加约束
# 1. 添加列级约束
# ALTER TABLE 表名 MODIFY COLUMN 字段名 字段类型 新约束L
# 2. 添加表级约束
# ALTER TABLE 表名 ADD 【CONSTRAINT 约束名】 约束类型(字段名) 外键的引用
1. 添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NOT NULL
2. 添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18
3. 添加主键
Ⅰ 列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY
Ⅱ 表级约束
ALTER TABLE stuinfo ADD UNIQUE (stuName)
4. 添加唯一键
Ⅰ 列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
Ⅱ 表级约束
ALTER TABLE stuinfo ADD UNIQUE (seat)
5. 添加外键
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorId) REFERENCES major(id)
修改表时删除约束
1. 删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) [NULL 可选]
2. 删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT
3. 删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY
4. 删除唯一键
ALTER TABLE stuinfo DROP INDEX uq [键名]
5. 删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major [键名]
列级约束 VS 表级约束
位置 支持的约束类型 是否可以起约束名
列级约束 列的后面 语法都支持,但没有外键效果 不可以
表级约束 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
标识列
# 又称为自增长列
# 含义:
可以不用手动地插入值,系统提供默认的序列值
# 特点:
# 1. 标识列必须与主键搭配吗? 不一定,但要求是一个key
# 2. 一个表可以有几个标识列? 至多一个
# 3. 标识列的类型只能是数值型
# 4. 标识列可以通过 SET AUTO_INCREMENT_INCREMENT = 1 设置增长步长
1. 创建表时设置标识列
create table if not exists myemployees.tab_identity
(
id int auto_increment
constraint `PRIMARY`
primary key,
NAME varchar(20) null
);
2. 修改表时设置标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT
3. 修改表时删除标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT
TCL语言(事务控制语言)
事务
定义:
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
举例:
转账
张三丰 1000
郭襄 1000
#包含的语句:
UPDATE 表 SET 张三丰的余额 = 500 WHERE NAME = '张三丰'
UPDATE 表 SET 郭襄的余额 = 500 WHERE NAME = '郭襄'
事务的ACID属性
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态边换到另一个一致性状态
3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事物之间不能相互干扰
4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库鼓掌不应该对其有任何影响
事务的创建
隐式事务:事务没有明显的开启和结束的标记
比如: insert、 update、 delete语句
显式事务:事务具有明显的开启和结束的标记
# 前提:必须先设置自动提交功能为禁用
# 步骤:
# Ⅰ 开启事务
SET autocommit = 0
START TRANSACTION [可选]
# Ⅱ 编写事务中的sql语句(SELECT INSERT UPDATE DELETE)
语句1,
语句2
…………
# Ⅲ 结束事务
# 两种方式:
COMMIT 提交事务
ROLLBACK 回滚事务
1. 演示事务的使用步骤:
# 开启事务
SET autocommit = 0;
START TRANSACTION ;
# 编写一组事务的语句
UPDATE account SET balance = 500 WHERE username = ‘张无忌’;
UPDATE account SET balance = 1500 WHERE username = '赵敏';
# 结束事务
COMMIT ;
2. DELETE 和 TRUNCATE 在事务使用时的区别
# 演示delete
set autocommit=0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
# 支持回滚
# 演示truncate
SET autocommit=0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;
# 不支持回滚
3. 演示 savepoint 的使用(在保存点与回滚语句之间的部分不会有效果)
SET AUTOCOMMIT =0;
START TRANSACTION ;
DELETE FROM dept2 WHERE department_id = 20;
SAVEPOINT a; #设置保存点
DELETE FROM dept2 WHERE department_id = 50;
ROLLBACK TO a;
SET AUTOCOMMIT = 1;
数据库的隔离级别
对于同时运行的多个事务,当这些事务访问'数据库中相同的数据'时,如果没有采取必要的隔离机制,就会导致各种并发问题:
# Ⅰ 脏读
对于两个事务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
#查看事务隔离级别的命令:
5.7 及之前:SELECT @@TX_ISOLATION
8 之后:SELECT @@TRANSACTION_ISOLATION
# 设置事务隔离级别
SET SESSION[或 GLOBAL] TRANSACTION ISOLATION LEVEL 隔离级别
视图
含义:
虚拟表,和普通表一样使用。
MySQL5.1 版本出现的新特性,是通过表动态生成的数据。只保存了SQL逻辑,不保存查询结果
应用场景:
多个地方用到同样的查询结果
该查询结果使用的SQL语句较为复杂
举例:
舞蹈班和普通班的对比
# 案例:查询姓张的学生名和专业名
# 以前的方法:
SELECT stuName, majorName
FROM stuinfo s
JOIN major m
ON s.majorId = m.id
WHERE stuName LIKE '张%'
# 使用视图:
CREATE VIEW V1
AS
SELECT stuName, majorName
FROM stuinfo s
JOIN major m
ON s.majorId = m.id
# 通过视图进行查询:
SELECT * FROM V1 WHERE stuName LIKE '张%'
视图的好处
1. 重用SQL语句
2. 简化复杂的SQL操作,不必知道它的查询细节
3. 保护数据,提高安全性
视图的修改
# 方式一:
CREATE or REPLACE VIEW 视图名
AS
查询语句
CREATE OR REPLACE VIEW V3
AS
SELECT AVG(salary), job_id
FROM employees
GROUP BY job_id
# 方式二:
# 语法:
# ALTER VIEW 视图名
# AS
# 查询语句
ALTER VIEW V3
AS
SELECT *
FROM employees
视图的查看和删除
# 1. 删除视图
语法:
DROP VIEW 视图名1,视图名2,………………
DROP VIEW v1,v2
# 2. 查看视图
DESC v1
或
SHOW CREATE VIEW V1
视图的更新(更改视图中的数据)
# 用到的视图
CREATE OR REPLACE VIEW mv1
AS
SELECT last_name, email
FROM employees
1. 插入(原始表中也会插入数据)
INSERT INTO mv1 VALUES ('DNG', 'DD@E.COM')
2. 修改(原始表也会修改)
UPDATE mv1 SET last_name = 'OPG' WHERE last_name = 'DNG'
3. 删除(原始表也会删除)
DELETE mv1 FROM mv1 WHERE last_name = 'OPG'
# 视图的可更新性
视图中包含以下类型是不能更新的:
1. 包含以下关键字的SQL语句:分组函数、 DISTINCT、 GROUP BY、 HAVING、 UNION 或者 UNION ALL
2. 常量视图
3. SELECT 中包含子查询的
4. JOIN
5. FROM 一个不能更新的视图
6. WHERE 子句的子查询引用了 FROM 子句中的表
# 验证:
1. 包含以下关键字的SQL语句:分组函数、 DISTINCT、 GROUP BY、 HAVING、 UNION 或者 UNION ALL
# 用到的视图
CREATE OR REPLACE VIEW V1
AS
SELECT MAX(salary) M, department_id
FROM employees
GROUP BY department_id
# 更新(The target table v1 of the UPDATE is not updatable)
UPDATE v1
SET M = 9000
WHERE department_id = 10
2. 常量视图
# 用到的视图
CREATE OR REPLACE VIEW V1
AS
SELECT 'John' name
# 更新(The target table v1 of the UPDATE is not updatable)
UPDATE v1
SET name = 'Lucy'
3. SELECT 中包含子查询的
# 用到的视图
CREATE OR REPLACE VIEW V1
AS
SELECT (SELECT MAX(salary) FROM employees) M
# 更新(The target table v1 of the UPDATE is not updatable)
UPDATE v1
SET M = 10000
4. JOIN
# 用到的视图
CREATE OR REPLACE VIEW V1
AS
SELECT last_name, department_name
FROM employees
JOIN departments d on d.department_id = employees.department_id
# 更新(可以更新,但不能插入)
UPDATE v1 SET last_name = '张飞' WHERE last_name = 'WHO'
INSERT INTO v1 VALUES ('FDAN','DFDSAFD')
5. FROM 一个不能更新的视图
# 用到的视图
V1 是上面 3 中的视图
CREATE OR REPLACE VIEW V2
AS
SELECT * FROM v1
# 更新(The target table v2 of the UPDATE is not updatable)
UPDATE v2
SET M = 60
6. WHERE 子句的子查询引用了 FROM 子句中的表
# 用到的视图
CREATE OR REPLACE VIEW V2
AS
SELECT last_name, email, salary
FROM employees
WHERE employee_id IN (SELECT manager_id
FROM employees
WHERE manager_id IS NOT NULL)
# 更新(The target table v2 of the UPDATE is not updatable)
UPDATE v2 SET salary=10000 WHERE last_name = 'K_ing'
练习
1. 查询邮箱中包含a字符的员工名、部门名和工种信息
CREATE VIEW V1
AS
SELECT CONCAT(last_name, ' ', first_name), department_name, job_title
FROM employees e
join departments d on d.department_id = e.department_id
join jobs j on j.job_id = e.job_id;
SELECT *
FROM v1
WHERE `CONCAT(last_name, ' ', first_name)` LIKE '%a%';
2. 查询各部门的平均工资级别
# Ⅰ 创建视图查看每个部门的平均工资
CREATE VIEW V2
AS
SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id
# Ⅱ 使用
SELECT v2.ag, g.grade_level
FROM v2
JOIN job_grades g
ON ag BETWEEN g.lowest_sal AND g.highest_sal
3. 查询平均工资最低的部门信息
SELECT *
FROM v2
ORDER BY ag
LIMIT 1
4. 查询平均工资最低的部门名和工资
SELECT d.*, v2.ag
FROM v2
JOIN departments d on v2.department_id = d.department_id
ORDER BY ag
LIMIT 1
5. 创建视图emp_v1,要求查询电话号码以'011'开头的员工姓名和工资、邮箱
CREATE OR REPLACE VIEW emp_v1
AS
SELECT CONCAT(last_name, ' ', first_name) name, salary, email
FROM employees
WHERE phone_number LIKE '011%'
SELECT * FROM emp_v1
6. 创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW emp_v2
AS
SELECT MAX(salary) mx_dep, e.department_id
FROM employees e
GROUP BY department_id
HAVING MAX(salary) > 12000
SELECT d.*, e.mx_dep
FROM departments d
join emp_v2 e on d.department_id = e.department_id
总结
创建语法的关键字 是否实际占用物理空间 使用
视图 CREATE VIEW 只是保存了SQL逻辑 增删改查,一般不能增删改
表 CREATE TABLE 保存了数据 增删改查
变量
分类
系统变量
全局变量
会话变量
自定义变量
用户变量
局部变量
系统变量
说明:变量由系统提供,不是用户定义,属于服务器层面。
语法:
1. 查看所有的系统变量
SHOW GLOBAL | [SESSION] VARIABLES
2. 查看满足条件的部分系统变量
SHOW GLOBAL | [SESSION] VARIABLES LIKE '%char%'
3. 查看指定的某个系统变量的值
SELECT @@GLOBAL | SESSION.系统变量名
4. 为某个系统变量赋值
方式一:
SET GLOBAL | [SESSION] 系统变量名 = 值
方式二:
SET @@GLOBAL | [SESSION].系统变量名 = 值
# 注意:
如果是全局级别,则需要加 GLOBAL, 如果是会话级别,则需要加 SESSION,如果不写,则默认 SESSION
全局变量
作用域:
服务器每次启动将为所有全局变量赋初始值,对所有的会话(连接)都有效,不能跨重启(重启以后会失效)
1. 查看所有的全局变量
SHOW GLOBAL VARIABLES
2. 查看部分全局变量
SHOW GLOBAL | [SESSION] VARIABLES LIKE '%char%'
3. 查看指定的全局变量
SELECT @@GLOBAL.AUTOCOMMIT
SELECT @@TRANSACTION_ISOLATION
4. 为某个指定的全局变量赋值
SET @@GLOBAL.AUTOCOMMIT = 0
会话变量
作用域:
仅仅针对于当前会话(连接)
1. 查看所有的会话变量
SHOW VARIABLES
SHOW SESSION VARIABLES
2. 查看部分的会话变量
SHOW VARIABLES LIKE '%char%'
SHOW SESSION VARIABLES '%char%'
3. 查看指定的某个会话变量
SELECT @@TRANSACTION_ISOLATION
4. 为某个会话变量赋值
方式一:
SET @@TRANSACTION_ISOLATION = 'read-uncommitted'
方式二:
SET SESSION TRANSACTION_ISOLATION = 'read-committed'
自定义变量
说明:
变量是用户自定义的,不是由系统提供的
# 使用步骤:
声明
赋值(操作符:= 或 :=)
使用(查看、比较、运算等)
# 作用域:
针对于当前会话(连接)有效,同于会话变量的作用域
可以应用在任何地方,也就是 BEGIN END 都可以
1. 声明并初始化
SET @用户变量名 = 值
SET @用户变量名 := 值
SELECT @用户变量名 := 值
2. 赋值(更新用户变量的值)
# 方式一:
通过 SET 或 SELECT
SET @用户变量名 = 值
SET @用户变量名 := 值
SELECT @用户变量名 := 值
# 例子:
SET @name = ‘john’
SET @name = 100
SET @count = 1
# 方式二:(要去查询出来的是一个值不能是一组值)
SELECT 字段 INTO 变量名
FROM 表
# 例子:
SELECT COUNT(*) INTO @count
FROM employees
3. 使用(查看用户变量的值)
SELECT @用户变量名
# 例子:
SELECT @count
局部变量
作用域:
仅仅在定义它的 BEGIN END 中有效
应用在 BEGIN END 中的第一句话
1. 声明
DECLARE 变量名 类型
DECLARE 变量名 类型 DEFAULT 值
2. 赋值
与上面讲的两种方式基本一样
# 不同点:
Ⅰ 方式一中:
SELECT @局部变量名 := 值
Ⅱ 方式二中:
SELECT 字段 INTO 局部变量名
FROM 表
3. 使用
SELECT 局部变量名
对比用户变量与局部变量
作用域 定义和使用的位置 语法
用户变量 当前会话 会话中的任何地方 必须加@符号
局部变量 BEGIN END 中 只能在 BEGIN END 中,且为第一句话 一般不用加@符号
# 练习
1. 声明两个变量赋初值,求和并打印
# 用户变量
SET @ONE =1;
SET @TWO = 2;
SELECT @ONE + @TWO SUM
# 局部变量
存储过程
存储过程和函数
存储过程和函数,类似于Java中的方法
优点:
1. 提高代码的重用性
2. 简化操作
存储过程
含义:
一组预先编译好的SQL语句的集合,理解成批处理语句
优点:
1. 提高代码的重用性
2. 简化操作
3. 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
语法:
1. 创建
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
方法体
END
# 注意:
Ⅰ 参数列表包含三部分
参数模式 参数名 参数类型
# 举例:
IN stuName VARCHAR(20);
# 参数模式:
IN 该参数可以作为输入,也就是该参数需要调用方传入值
OUT 该参数可以作为输出,也就是该参数可以作为返回值
INOUT 该参数既可以作为输入也可以作为输出,也就是该参数既可以传入值,也可以返回值
Ⅱ 如果存储过程体只有一句话, BEGIN END 可以省略
① 存储过程体中的每条SQL语句的结尾要求必须加分号
② 存储过程的结尾可以使用 DELIMITER 重新设置
DELIMITER 结束标记
语句
DELIMITER $
2. 调用
CALL 存储过程名(实参列表)
练习
1. 空参列表
# 案例:
插入到admin表中五条记录
#Ⅰ 定义:
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username, password) values ('John', '123456');
INSERT INTO admin(username, password) values ('Rose', '123456');
INSERT INTO admin(username, password) values ('Jack', '123456');
INSERT INTO admin(username, password) values ('Tom', '123456');
INSERT INTO admin(username, password) values ('King', '123456');
END $
# Ⅱ 调用
CALL myp1();
2. 创建一个带 IN 模式参数的存储过程
# 案例1:
创建存储过程实现:根据女神名,查询对应的男神信息
# Ⅰ 定义
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
SELECT b.*
FROM boys b
RIGHT JOIN beauty ON b.id = beauty.boyfriend_id
WHERE beauty.name = beautyName;
END;
# Ⅱ 调用
CALL myp2('小昭')
# 案例2:
创建存储过程实现:用户是否登录成功
# Ⅰ 定义
CREATE PROCEDURE myp3(IN username VARCHAR(20), IN password VARCHAR(20))
BEGIN
DECLARE result INT DEFAULT 0;# 声明并初始化
SELECT COUNT(*)
INTO result # 赋值
FROM exer.admin
WHERE admin.username = username
AND admin.password = password;
SELECT IF(result > 0, '成功', '失败'); # 使用
END;
# Ⅱ 调用
CALL myp3('John', 88888)
3. 创建带 OUT 模式的存储过程
# 案例1:
根据女神名,返回对应的男神名
# Ⅰ 定义
USE girls;
CREATE PROCEDURE myp5(IN beautyName VARCHAR(20), OUT boyName VARCHAR(20))
BEGIN
SELECT bo.boyName INTO boyName
FROM boys bo
JOIN beauty b ON b.boyfriend_id = bo.id
WHERE b.name = BeautyName;
END;
# Ⅱ 调用
CALL myp5('小昭',@bName);
SELECT @bName
# 案例2:
根据女神名,返回对应的男神名和男神魅力值
# Ⅰ 定义
CREATE PROCEDURE myp6(IN beautyName VARCHAR(20), OUT boyName VARCHAR(20), OUT userCP INT)
BEGIN
SELECT bo.boyName, bo.userCP
INTO boyName, userCP
FROM boys bo
JOIN beauty b ON b.boyfriend_id = bo.id
WHERE b.name = BeautyName;
END;
# Ⅱ 调用
CALL myp6('小昭', @boyName, @userCP);
SELECT @boyName,@userCP;
4. 创建带inout模式参数的存储过程
# 案例1:
传入a和b两个值,最终a和b都翻倍并返回
# Ⅰ 定义
CREATE PROCEDURE myp7(INOUT a INT, INOUT b INT)
BEGIN
SET a = a * 2;
SET b = b * 2;
END;
# Ⅱ 调用
SET @M = 10;
SET @N = 20;
CALL myp7(@M, @N);
SELECT @M, @N
存储过程练习
1. 创建存储过程实现传入用户名和密码,插入到admin表中
# Ⅰ 定义
CREATE PROCEDURE p1(IN username VARCHAR(20), IN passport VARCHAR(20))
BEGIN
INSERT INTO admin(admin.USERNAME, admin.PASSWORD) VALUES (username, passport);
end;
# Ⅱ 调用
CALL p1('admin', '000000');
2. 创建存储过程实现传入女神编号,返回女神名称和女神电话
# Ⅰ 定义
CREATE PROCEDURE p2(IN girlID INT, OUT reGirlName VARCHAR(20), OUT reGirlPhone VARCHAR(20))
BEGIN
SELECT name, phone
INTO reGirlName,reGirlPhone
FROM beauty
WHERE id = girlID;
END;
# Ⅱ 调用
CALL p2(1, @name, @phone);
SELECT @name, @phone
3. 创建存储过程或函数实现传入两个女神生日,返回大小
# Ⅰ 定义
CREATE PROCEDURE p3(IN bDay1 DATE, IN bDay2 DATE, OUT result INT)
BEGIN
SELECT DATEDIFF(bDay1, bDay2) INTO result;
END;
# Ⅱ 调用
CALL p3('1998-08-14', NOW(),@result);
SELECT if(@result > 0, '大', '小') result
4. 创建存储过程或函数实现传入一个日期,格式化成XX年XX月XX日并返回
# Ⅰ 定义
CREATE PROCEDURE P4(IN myDate DATETIME, OUT result VARCHAR(50))
BEGIN
SELECT DATE_FORMAT(myDate, '%y年%m月%d日') INTO result;
END;
# Ⅱ 调用
CALL P4(NOW(),@result);
SELECT @result
5. 创建存储过程或函数实现传入女神名返回:女神 and 男神 格式的字符串
# Ⅰ 定义
CREATE PROCEDURE P5(IN GirlName VARCHAR(20), OUT result VARCHAR(100))
BEGIN
SELECT CONCAT(GirlName, ' AND ', IFNULL(boyName, 'NULL'))
FROM boys bo
RIGHT JOIN beauty
ON bo.id = beauty.boyfriend_id
WHERE name = GirlName;
END;
# Ⅱ 调用
CALL P5('小昭', @result);
SELECT @result
6. 创建存储过程或函数,根据传入的条目数和起始索引,查询beauty表的记录
CREATE PROCEDURE P6(IN size INT, IN OFFSETS INT)
BEGIN
SELECT * FROM beauty LIMIT OFFSETS, size;
END;
删除存储过程
语法:
DROP PROCEDURE 存储过程名
DROP PROCEDURE p2
查看存储过程的信息
SHOW CREATE PROCEDURE p1;
函数
与存储过程的区别:
存储过程:可以有 0 个返回,也可以有多个返回,适合做批量插入、批量更新
函数:有 1 个返回,适合做处理数据后返回一个结果
# 语法:
1. 创建
CREATE FUNCTION 函数名(函数列表) RETURNS 返回类型
BEGIN
函数体
END
# 注意:
①. 参数列表包含两部分:
Ⅰ 参数名
Ⅱ 产生类型
②. 函数体:肯定会有 RETURN 语句,如果没有会报错
如果 RETURN 语句没有放在函数体的最后也不报错,但不建议
③. 函数体中仅有一句话,则可以省略 BEGIN END
④. 使用 DELIMITER 语句设置结束标记
# 调用
SELECT 函数名(参数列表)
查看函数
SHOW CREATE FUNCTION F1;
删除函数
DROP FUNCTION F1
练习
1. 无参数有返回值
# 案例:
返回公司的员工个数
# Ⅰ 定义:
# 第一行用于解决以下错误:
# This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
SET GLOBAL log_bin_trust_function_creators=TRUE;
CREATE FUNCTION F1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0;
SELECT COUNT(*) INTO c
FROM employees;
RETURN c;
END;
# Ⅱ 调用
SELECT F1();
2. 有参数有返回值
# 案例1:
根据员工名,返回对应的工资
CREATE FUNCTION F2(emName VARCHAR(20)) RETURNS DOUBLE
BEGIN
SET @SAL = 0; # 定义用户变量
SELECT salary
INTO @SAL # 赋值
FROM employees
WHERE last_name = emName;
RETURN @SAL;
END;
SELECT F2('k_ing');
# 案例2:
根据部门名,返回该部门的平均工资
# Ⅰ 定义
CREATE FUNCTION F3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
DECLARE SAL DOUBLE;
SELECT AVG(salary)
INTO SAL
FROM employees
JOIN departments d on d.department_id = employees.department_id
WHERE department_name = deptName;
RETURN SAL;
END;
# Ⅱ 调用
SELECT F3('IT');
3. 创建函数,实现传入两个 FLOAT, 返回二者之和
CREATE FUNCTION F1(num1 FLOAT, num2 FLOAT) RETURNS FLOAT
BEGIN
DECLARE M FLOAT DEFAULT 0.0;
SELECT num1 + num2 INTO M;
RETURN M;
end;
SELECT F1(1.25,6.21);
流程控制结构
顺序结构
程序从上往下依次执行
分支结构
定义:
程序从两条或多条路径中选择一条去执行
1. IF 函数
功能:实现简单的双分支
语法:
IF(表达式1, 表达式2, 表达式3)
执行顺序:
如果表达式1成立,则 IF 函数返回表达式2的值,否则返回表达式3的值
应用:
任何地方
2. CASE 结构
情况一:
类似于Java中的switch语句,一般用于实现等值判断
位置:
BEGIN END 中
BEGIN END 外面
语法:
CASE 变量 | 表达式 | 字段
WHEN 要判断的值 THEN 返回的值1或 [语句1]
WHEN 要判断的值 THEN 返回的值2或 [语句2]
………………………………………………………………
ELSE 要返回的值n或 [语句n]
END [CASE]
情况二:
类似于Java中的多重 IF 语句,一般用于实现区间判断
语法:
CASE
WHEN 要判断的值 THEN 返回的值1或 [语句1]
WHEN 要判断的值 THEN 返回的值2或 [语句2]
………………………………………………………………
ELSE 要返回的值n或 [语句n]
END [CASE]
# 特点:
Ⅰ
可以作为表达式,嵌套在其他语句中使用,可以放在任何地方, BEGIN END 中或 BEGIN END 的外面
可以作为独立的语句去使用,只能放在 BEGIN END 中
Ⅱ
如果 WHEN 中的值满足或条件成立,则执行对应的 THEN 后面的语句,并且结束 CASE
如果都不满足,则执行 ELSE 中的语句或值
Ⅲ
ELSE 可以省略,如果 ELSE 省略了,并且所有 WHEN 条件都不满足,则返回 NULL
3. IF 结构
功能:
实现多重分支
语法:
IF 条件1 THEN 语句1;
ELSEIF 条件2 THEN 语句2;
练习
# CASE 结构
1. 创建存储过程,根据传入的成绩来显示等级。比如:传入的成绩在 90 - 100 之间,显示A,以此类推。
CREATE PROCEDURE P1(IN grades INT)
BEGIN
CASE
WHEN grades >= 90 AND grades <= 100 THEN SELECT 'A';
WHEN grades >= 80 THEN SELECT 'B';
WHEN grades >= 60 THEN SELECT 'C';
ELSE SELECT 'D';
END CASE;
END;
CALL P1(50);
# IF 结构
1. 根据传入的成绩,来显示等级,比如:传入的成绩为 90 - 100,返回A, 80 - 90 返回B, 60 - 80 返回C,否则,返回D
# Ⅰ 定义
CREATE FUNCTION TEST_IF(score INT) RETURNS CHAR
BEGIN
IF score >= 90 AND score <= 100 THEN
RETURN 'A' ;
ELSEIF score >= 80 THEN
RETURN 'B';
ELSEIF score >= 60 THEN
RETURN 'C';
ELSE
RETURN 'D';
END IF;
END;
# Ⅱ 调用
SELECT TEST_IF(69);
循环结构
程序在满足一定条件的基础上,重复执行一段代码
分类:
1. WHILE
2. LOOP
3. REPEAT
循环控制
ITERATE 类似于 continue, 继续,结束本次循环,继续下一次
LEAVE 类似于 break, 跳出,结束当前所在的循环
语法:
1. WHILE
[标签:]WHILE 循环条件 DO
循环体
END WHILE [标签]
2. LOOP
[标签:]LOOP
循环体;
END LOOP[标签]
可以用来模拟简单的死循环
3. REPEAT
[标签:]REPEAT
循环体
UNTIL 结束循环的条件
END REPEAT[标签]
练习
# 没有添加循环控制语句
1. 批量插入,根据次数向admin表中插入多条记录
CREATE PROCEDURE pro_while(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE (i < insertCount)
DO
INSERT INTO admin(username, password) VALUES (CONCAT('ROSE', i), '666');
SET i = i + 1;
END WHILE;
END;
CALL pro_while(10);
# 添加 LEAVE 语句
2. 批量插入,根据次数向admin表中插入多条记录,如果次数>20则停止
TRUNCATE TABLE admin;
DROP PROCEDURE pro_while;
CREATE PROCEDURE pro_while(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 0;
a:
WHILE i <= insertCount
DO
INSERT INTO girls.admin(username, password) VALUES (CONCAT('小花', i), '6666');
SET i = i + 1;
IF i >= 20 THEN
LEAVE a;
END IF;
END WHILE a;
END;
CALL pro_while(100)
# 添加ITERATE语句
3. 批量插入,根据次数向admin表中插入多条记录,并且插入偶数值
TRUNCATE TABLE admin;
DROP PROCEDURE pro_while;
CREATE PROCEDURE pro_while(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 0;
a:
WHILE i <= insertCount
DO
SET i = i + 1;
IF MOD(i, 2) != 0 THEN
ITERATE a;
END IF;
INSERT INTO girls.admin(username, password) VALUES (CONCAT('小花', i), '6666');
END WHILE a;
END;
CALL pro_while(100)
4. 已知表stringcontent,其中字段:di 自增长, content VARCHAR(20),要求:
向该表插入指定个数的、随机的字符串
# 建表
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
id int primary key auto_increment,
content VARCHAR(20)
)
# 创建存储过程
CREATE PROCEDURE test_randStr_insert(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnoprstuvwxyz';
DECLARE startIndex INT DEFAULT 1; # 代表起始索引
DECLARE length INT DEFAULT 1;# 代表截取长度
WHILE i <= insertCount
DO
SET length = FLOOR(RAND() * (20 - startIndex + 1) + 1);#产生一个随机整数,代表截取长度 1 - (26 - startIndex + 1)
SET startIndex = FLOOR(RAND() * 26 + 1);# 产生一个随机整数,代表起始索引 1 - 26
INSERT INTO stringcontent VALUE (NULL, SUBSTR(str, startIndex, length));
SET i = i + 1;
END WHILE;
END;
# 调用存储过程
CALL test_randStr_insert(20)