Mysql入门学习笔记,包含简单查询、常用函数、分组查询、多表查询、(嵌套)子查询、分页查询、联合查询、表中数据的增删改(DML)、表和库的增删改(DDL)、数据类型、常见约束、标识列、事务(TCL)与视图、变量、存储过程、函数和流程控制等相关内容。
根据b站上视频整理,作为入门级的资料,带有案例库和练习原码,方便实际操练,与大家交流学习。
一、简单查询
show databases;#查看所有库
use myemployees;#调用myemployees库
# 基础查询 select命令 select...from...
SELECT last_name FROM employees;
SELECT last_name,salary,email FROM employees;
SELECT `department_name`,`department_name` FROM departments; #查询效果
SELECT 100;
SELECT 'join'; #打印效果
SELECT 100*99;
SELECT 100%99; #求余数
SELECT VERSION(); #查询版本函数
DESC departments; #desc 描述表
# 字段起别名
SELECT 100*99 AS 结果; # as或用空格 起别名
SELECT last_name AS 姓,first_name AS 名 FROM employees;
SELECT last_name 姓,first_name 名 FROM employees;
SELECT salary AS 'out put' FROM `employees`;
# distinct 去重
SELECT DISTINCT department_id FROM `employees`;
# 加号的作用:仅为运算符
SELECT 100+999 结果;
SELECT 'join'+99 打印;
SELECT NULL+99 结果; # 只要有一方为null,结果为null
# concat()拼接函数,+号无法拼接
SELECT `last_name`+`first_name` 姓名 FROM employees;
SELECT CONCAT(`last_name`,',',`first_name`) 姓名 FROM employees; # 用逗号隔开
# ifnull()函数 如果为null,作为0,否则正常
SELECT
IFNULL(`commission_pct`,0) AS 奖金率,
commission_pct
FROM
employees;
# 条件查询 select...from...where...
/* where后面的筛选条件有:
条件运算符: > < = <> != >= <=
逻辑运算符: and or not
模糊查询: like
between and
in
is null
*/
# 一、条件运算符
# 查询工资大于12000的员工信息
SELECT * FROM employees WHERE salary>12000;
# 查询部门编号≠90的员工姓名和部门编号
SELECT
CONCAT(`first_name`,`last_name`),`department_id`
FROM
employees
WHERE
`department_id`<>90;
# 二、逻辑运算符
# 查询工资在10000到20000之间的员工名、工资以及奖金
SELECT
`last_name`,`salary`,`commission_pct`
FROM
employees
WHERE
`salary`>10000 AND 20000;
# 查询部门编号不在90到110之间,或者工资高于15000的员工
SELECT
`last_name`
FROM
employees
WHERE
NOT(`department_id`>=90 AND `department_id`<=110) OR (`salary`>15000);
# 三、模糊查询
# 查询员工名中包含字符a的员工信息
SELECT * FROM employees WHERE `last_name` LIKE '%a%';
# 查询员工名中第三个字符为c,第五个字符为a的员工信息
SELECT
*
FROM
employees
WHERE
last_name LIKE '__c_a%';
# 查询员工名中第二个字符为_的员工信息
SELECT
*
FROM
employees
WHERE
last_name LIKE '_\_%';
# 查询员工编号在100到120内的员工信息
SELECT
*
FROM
employees
WHERE
employee_id BETWEEN 100 AND 120; # between...and...顺序不能颠倒
# 查询工作种类是PU_CLERK和ST_MAN的员工信息
SELECT
*
FROM
employees
WHERE
job_id IN ('PU_CLERK','ST_MAN');
# 查询奖金为null的员工名和奖金
SELECT
last_name,commission_pct
FROM
employees
WHERE
commission_pct IS NULL;
# 安全等于 <=>
SELECT
last_name,commission_pct
FROM
employees
WHERE
commission_pct <=> NULL;
# 排序查询
/* select 查询列表(3)
from 表(1)
where 筛选条件(2)
order by 排序列表 [asc/desc](4)
*/
SELECT * FROM employees ORDER BY salary DESC; # desc降序
SELECT * FROM employees ORDER BY salary ASC; # asc升序
# 按年薪将序
SELECT * ,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY 年薪 DESC;
# 按函数排序
SELECT LENGTH(last_name) 字节长度,last_name,salary
FROM employees
ORDER BY LENGTH(last_name) DESC;
# 多个字段排序
SELECT *
FROM employees
ORDER BY salary ASC,employee_id DESC;
#----------------------------------------------------------------------
二、常用函数
/* 1.单行函数:length,concat,ifnull,isnull (传入一个值传出一个值)
2.分组函数:做统计使用(传入多个值传出一个值,如求均值) */
#-------------------------------单行函数-----------------------------------
# 一、常见函数
# length 长度
SELECT LENGTH('aa'); #2
SELECT LENGTH('对a');#3+1=4
SHOW VARIABLES LIKE '%char%';
# concat 拼接
SELECT CONCAT(last_name,'_',first_name) 姓名
FROM employees;
# upper、lower 大小写
SELECT UPPER('john');#JOHN
SELECT LOWER('joHn');#john
SELECT # 姓大写,名小写,两者拼接
CONCAT(UPPER(last_name),'_',LOWER(first_name)) 姓名
FROM
employees;
# substr 字符串截取
#截取从指定索引处后所有的字符
SELECT SUBSTRING('李莫愁爱上了陆展元',7) output;#陆展元
#截取从指定索引处后指定长度的字符,注意,都是字符,而不是字节
SELECT SUBSTRING('李莫愁爱上了陆展元',1,3) output;#李莫愁
# instr 返回子串第一次出现的索引,如果找不到,返回0
SELECT INSTR('杨不殷爱上殷六侠','殷六侠') AS output;
# trim 删除两端指定字符
SELECT TRIM(" zhang ") AS output; #删除两端空格
SELECT TRIM('a' FROM "aaaaazhang aaa") AS output; #删除两端指定值a
# lpad 用指定字符实现左填充指定长度
SELECT LPAD('殷素数',10,'*') AS output; #长度不足,左填充
SELECT LPAD('殷素数',2,'*') AS output; #长度超过,截取
# rpad 用指定字符实现右填充指定长度
SELECT RPAD('殷素数',10,'*') AS output; #长度不足,右填充
SELECT RPAD('殷素数',2,'*') AS output; #长度超过,截取
# replace 替换
SELECT REPLACE("张无忌爱上周芷若,周芷若",'周芷若','赵敏');
# 二、数学函数
# round 四舍五入
SELECT ROUND(-1.50);# -2
SELECT ROUND(1.567,2);# 指定取两位小数
# ceil 向上取整
SELECT CEIL(1.0);#1
SELECT CEIL(1.2);#2
SELECT CEIL(-1.2);#-1
# floor 向下取整
SELECT FLOOR(9.9);#9
SELECT FLOOR(-9.9);#-10
SELECT CEIL(1.0);#1
# truncate 截断
SELECT TRUNCATE(1.999,1);# 小数点后保留1位(不进行四舍五入)
# mod 取余数
SELECT MOD(10,3); # 10/3余数为1
# rand 随机数
SELECT RAND(); 区间取0不取1 [0,1)
# 三、日期函数
SELECT NOW(); #日期+时间
SELECT CURDATE(); # 只有日期
SELECT CURTIME(); # 只有时间
# 获取指定部分年,月,日,小时,分钟,秒
SELECT YEAR(NOW());#年
SELECT YEAR(hiredate) 年 FROM employees; # 表中的年
SELECT MONTH(NOW());# 月
SELECT MONTHNAME(NOW()); # 英文显示月份
SELECT DAY(NOW());# 日
SELECT HOUR(NOW());# 小时
SELECT MINUTE(NOW());# 分钟
SELECT SECOND(NOW());# 秒
# str_to_date 将字符串转化为时间
SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS output; # Y四位数年,c月,d日
SELECT * FROM employees WHERE hiredate=STR_TO_DATE('4-3 1992','%c-%d %Y');
# date_format 将时间转化为字符串
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS output;# y二位数年,m月,d日
SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期
FROM employees
WHERE commission_pct IS NOT NULL;
# 四、其他函数
SELECT VERSION(); # 显示当前系统版本
SELECT DATABASE(); # 显示当前数据库
SELECT USER(); # 显示当前用户
#五、流程控制函数
# if函数 :if else效果
SELECT IF(10>5,'大','小');
SELECT `last_name`,IF(`commission_pct` IS NULL,'没奖金','有奖金') FROM employees;
# case函数 (判断的字段在case后)
/* case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end */
/* 案例:查询员工的工资,要求
部门号=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;
# case 做多重if使用 (判断的字段在when后)
/* case
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
end */
/* 案例 : 查询员工的工资情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资>10000,显示C级别
否则,显示D级别 */
SELECT `last_name`,`salary`,
CASE
WHEN salary>20000 THEN 'A级别'
WHEN salary>15000 THEN 'B级别'
WHEN salary>10000 THEN 'C级别'
ELSE 'D级别'
END AS 工资级别
FROM employees;
#--------------------------------练习练习练习---------------------------------------------
# 显示系统时间(日期+时间)
SELECT NOW();
# 查询员工号、姓名、工资以及工资提高百分之20%后的结果
SELECT `employee_id`,`last_name`,`salary`,`salary`*1.2 新工资 FROM employees;
# 将员工的姓名按首字母排序,并写出姓名长度
SELECT LENGTH(last_name) 长度, SUBSTR(`last_name`,1,1) 首字符
FROM employees
ORDER BY SUBSTR(`last_name`,1,1);
# 使用case-when
SELECT job_id,
CASE job_id
WHEN 'AD_PRES' THEN 'A'
WHEN 'ST_MAN' THEN 'B'
WHEN 'IT_PROG' THEN 'C'
ELSE job_id
END
FROM employees;
#-----------------------------------分组函数---------------------------------------------
# 简单使用
SELECT SUM(`salary`) FROM employees; # 工资之和
SELECT AVG(`salary`) FROM employees; # 平均数
SELECT MIN(`salary`) FROM employees; # 最小值
SELECT MAX(`salary`) FROM employees; # 最大值
SELECT COUNT(`salary`) FROM employees; # 计数
/* 1、sum、avg一般用于处理数值型 (不计算null值)
2、min、max、count支持任何类型
3、和分组函数一同查询的字段要求是group by后的字段 */
# 和distinct搭配
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
SELECT COUNT(DISTINCT `commission_pct`),COUNT(`commission_pct`) FROM employees;
# count函数的详细命令
SELECT COUNT(*) FROM employees; # 统计行数
SELECT COUNT(1) FROM employees; # 与上同
#----------------------------------练习练习练习------------------------------------------
# 查询公司员工工资的最大、最小、平均值,求总和
SELECT MAX(`salary`),MIN(`salary`),AVG(`salary`),SUM(`salary`) FROM employees;
# 查询员工表中的最大入职时间和最小入职时间的相差天数
SELECT DATEDIFF(MAX(`hiredate`),MIN(`hiredate`)) FROM employees;
SELECT DATEDIFF(NOW(),'1996-5-29'); # 查看自己活了多少天
# 查询部门编号为90的员工个数
SELECT COUNT(*) FROM employees WHERE `department_id`=90;
#---------------------------------------------------------------------------------------
三、分组查询
/* # 分组查询 group by
select 分组函数,列(要求出现在group by后面)
from 表
where 筛选条件(原始表源)
group by 分组的列(单个、多个字段都可以)
having 条件 (分组后结果集)
order by 排序子句 */
# 查询每个工种的最高工资
SELECT MAX(`salary`),`job_id`
FROM employees
GROUP BY `job_id`;
# 查询每个位置上的部门个数
SELECT COUNT(*),`location_id`
FROM departments
GROUP BY `location_id`;
# 查询邮箱中包含a字符的,每个部门的平均工资
SELECT AVG(`salary`),`department_id`
FROM employees
WHERE `email` LIKE '%a%'
GROUP BY `department_id`;
# 查询有奖金的每个领导手下员工的最高工资
SELECT MAX(`salary`),`manager_id`
FROM employees
WHERE `commission_pct` IS NOT NULL
GROUP BY `manager_id`;
# 查询哪个部门的员工个数>2
SELECT COUNT(*),`department_id`
FROM employees
GROUP BY `department_id`# 查询每个部门的员工数
HAVING COUNT(*)>2; # having 找出个数>2的
# 查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT MAX(`salary`),`job_id`
FROM employees
WHERE `commission_pct` IS NOT NULL
GROUP BY `job_id`
HAVING MAX(`salary`) > 12000;
# 查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个
SELECT MIN(`salary`),`manager_id`
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 AVG(`salary`) AS 工资,`department_id`,`job_id`
FROM employees
GROUP BY `department_id`,`job_id`;
# 添加排序
# 查询每个部门每个工种的平均工资,按平均工资的高低显示出来
SELECT AVG(`salary`) AS 工资,`department_id`,`job_id`
FROM employees
GROUP BY `department_id`,`job_id`
ORDER BY AVG(`salary`) DESC;
#---------------------------------练习练习练习-------------------------------------------
# 查询各job_id的员工工资的最大、最小、平均值、总和,并按job_id升序
SELECT MAX(`salary`),MIN(`salary`),AVG(`salary`),SUM(`salary`),`job_id`
FROM employees
GROUP BY `job_id`
ORDER BY `job_id`;
# 查询员工最高工资和最低工资的差距
SELECT MAX(`salary`)-MIN(`salary`) FROM employees;
# 查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT MIN(`salary`),`manager_id`
FROM employees
WHERE `manager_id` IS NOT NULL
GROUP BY `manager_id`
HAVING MIN(`salary`) > 6000;
# 查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
SELECT AVG(`salary`) 平均工资,COUNT(*) 员工数量,`department_id`
FROM employees
GROUP BY `department_id`
ORDER BY 平均工资 DESC;
# 选择具有各个job_id的员工人数
SELECT COUNT(*) 人数,`job_id`
FROM employees
GROUP BY `job_id`;
#---------------------------------------------------------------------------------------
四、多表查询
/* 多表查询:查询的字段来自多个表 select...from...where(连接条件)...
笛卡尔乘积现象:表1有m行,表2有n行,结果为m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件
分类:
按年代分类:sql92标准(内连接)、sql99标准(内连接+左右外连接+交叉连接)
按功能分类:
内连接:等值连接、非等值连接、自连接
外连接:左外连接、右外连接、全外连接
交叉连接
*/
SELECT * FROM beauty;
SELECT * FROM boys;
# 一、sq192标准
/*1、等值连接
①、多张表等值连接的结果为多表的交集部分
②、n表连接至少需要n-1个连接条件
③、多表的顺序没有要求
④、一般需要起别名 */
# 查询女神名和对应的男神名
SELECT NAME,boyName
FROM beauty,boys
WHERE beauty.boyfriend_id = boys.id;
# 查询员工名和对应部门名
SELECT `last_name`,`department_name`
FROM employees,departments
WHERE employees.`department_id`=departments.`department_id`;
# 为表起别名,提高简洁度
# 查询员工名、工种号、工种名
SELECT `last_name`,e.`job_id`,j.`job_title`
FROM employees e,jobs j
WHERE e.`job_id`=j.`job_id`;
# 加筛选
# 查询有奖金的员工名、部门名、奖金
SELECT `last_name`,`department_name`,`commission_pct`
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id` AND `commission_pct` IS NOT NULL;
# 查询城市名中第二个字符为o的部门名和城市名
SELECT department_name,city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id` AND city LIKE '_o%';
# 加分组
# 查询每个城市的部门个数
SELECT COUNT(*),l.city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
GROUP BY city;
# 查询有奖金的每个部门的部门名、领导编号和该部门的最低工资
SELECT MIN(salary),d.`manager_id`,d.`department_name`
FROM employees e,departments d
WHERE e.`department_id`= d.`department_id` AND `commission_pct` IS NOT NULL
GROUP BY d.`department_name`,d.`manager_id`;
# 加排序
# 查询每个工种的工种名和员工个数,并且按员工个数降序
SELECT COUNT(*),j.`job_title`
FROM employees e,jobs j
WHERE e.`job_id` = j.`job_id`
GROUP BY `job_title`
ORDER BY COUNT(*) DESC;
# 实现三表连接
# 查询员工名、部门名和所在城市
SELECT `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`;
# 2、非等值连接
# 查询员工的工资和工资级别
SELECT salary,`grade_level` 等级
FROM employees e,job_grades g
WHERE salary BETWEEN g.`grade_level` AND g.`highest_sal`; # 提前查看表中结构
# 3、自连接
# 查询员工名和上级的名称
SELECT e.`employee_id` '上级ID',e.`last_name` '上级名',m.`employee_id`,m.`last_name`
FROM employees e,employees m # 两张一样的表,一张叫e,一张叫m
WHERE e.`employee_id`=m.`manager_id`;
#--------------------------------练习练习练习----------------------------------------
# 显示员工表的最大工资、工资平均值
SELECT MAX(`salary`),AVG(`salary`) FROM employees;
# 查询员工表的`employee_id`、`job_id`、`last_name`,按`department_id`降序,`salary`升序
SELECT `employee_id`,`job_id`,`last_name`
FROM employees
ORDER BY `department_id` DESC,`salary`;
# 查询员工表的`job_id`中包含a和e的,并且a在e前面
SELECT `job_id`
FROM employees
WHERE `job_id` LIKE '%a%e%';
# 显示所有员工的姓名、部门号和部门名称
SELECT `last_name`,d.`department_id`,d.`department_name`
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id`;
# 查询90号部门员工的`job_id`和90号部门的`location_id`
SELECT `job_id`,`location_id`
FROM employees e,departments d
WHERE e.`department_id`=d.`department_id` AND d.`department_id`=90;
# 选择所有有奖金员工的`last_name`,`department_name`,`location_id`,`city`
SELECT `last_name`,`department_name`,d.`location_id`,`city`
FROM employees e,departments d,locations l
WHERE e.`department_id`=d.`department_id` AND d.`location_id`=l.`location_id`
AND e.`commission_pct` IS NOT NULL;
# 选择city在Toronto工作的员工的`last_name`,`job_id`,`department_id`,`department_name`
SELECT `last_name`,`job_id`,e.`department_id`,`department_name`
FROM employees e,departments d,locations l
WHERE e.`department_id`=d.`department_id`
AND d.`location_id`=l.`location_id`
AND l.`city`='Toronto';
# 查询每个工种、每个部门的部门名、工种名和最低工资
SELECT `department_id`,`job_title`,MIN(`salary`)
FROM employees e,departments d,jobs j
WHERE e.`department_id`=d.`department_id` AND e.`job_id`=j.`job_id`
GROUP BY `job_title`,`department_name`;
# 查询每个国家下的部门个数大于2的国家编号
SELECT `country_id`,COUNT(*)
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
GROUP BY `country_id`
HAVING COUNT(*)>2;
/* 选择指定员工的姓名、员工号,以及他的管理者的姓名和员工号,结果类似下面形式
employees Emp# manager Mgr#
kochhar 101 king 100 */
SELECT e.`last_name` 'employees',e.`employee_id` 'Emp#',m.`last_name` 'manager',m.`employee_id` 'Mgr#'
FROM employees e,employees m
WHERE e.`manager_id`=m.`employee_id`
AND e.`last_name` = 'kochhar';
#---------------------------------------------------------------------------------------
/* 二、sql99语法
select 查询列表
from 表1 别名【连接类型】
join 表2 别名
on 连接条件
where 筛选条件
group by 分组
having ...
order by ...
分类(放在【连接类型】中):
1、内连接(不分主子表):inner
2、外连接(分主子表):
左外:left
右外:right
全外:full
3、交叉连接:cross
*/
/* 1、内连接
select 查询列表
from 表1 别名
inner join 表2 别名
on 连接条件
分类:等值
非等值
自连接
特点:
①inner可以省略
②筛选条件放where后面,连接条件放on后面
③inner join连接和sql92语法中的等值连接效果一样
*/
# 等值连接
# 查询员工名、部门名
SELECT `last_name`,`department_name`
FROM employees e INNER JOIN departments d
ON e.`department_id`=d.`department_id`;
# 查询名字中包含e的员工名和工种名
SELECT `last_name`,`job_title`
FROM employees e INNER JOIN jobs j
ON e.`job_id`=j.`job_id`
WHERE last_name LIKE '%e%';
# 查询部门个数>3的城市名和部门个数
SELECT `city`,COUNT(*)
FROM departments d INNER JOIN locations l
ON d.`location_id`=l.`location_id`
GROUP BY `city`
HAVING COUNT(*)>3;
# 查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序
SELECT `department_name`,COUNT(*)
FROM departments d INNER JOIN employees e
ON d.`department_id`=e.`department_id`
GROUP BY d.`department_name`
HAVING COUNT(*) > 3
ORDER BY COUNT(*) DESC;
# 查询员工名、部门名、工种名,并按部门名降序
SELECT `last_name`,d.`department_name`,`job_title`
FROM employees e INNER JOIN departments d
ON e.`department_id`=d.`department_id`
INNER JOIN jobs j
ON e.`job_id`=j.`job_id`
ORDER BY `department_name` DESC;
# 非等值连接
# 查询员工的工资级别
SELECT `salary`,`grade_level`
FROM employees e INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`;
# 查询工资级别个数>20的个数,并且按工资级别降序
SELECT `grade_level` 级别,COUNT(*) 个数
FROM employees e INNER JOIN job_grades g
ON `salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`
GROUP BY `grade_level`
HAVING COUNT(*) > 20
ORDER BY `grade_level` DESC;
# 自连接
# 查询姓名中包含字符k的员工的名字、上级的名字
SELECT e.`last_name` 上级,m.`last_name` 员工
FROM employees e INNER JOIN employees m
ON e.`employee_id` = m.`manager_id`
WHERE m.`last_name` LIKE '%k%';
/* 2、外连接
应用场景:用于查询一个表中有,另一个表中没有的记录
外连接的查询结果为主表中的所有记录
如果有匹配的显示值;如果没匹配的显示null
左外连接,left join左边的是主表
右外连接,right join右边的是主表
*/
# 查询没有男朋友的女神名(左外连接)
SELECT b.`name`,bo.`boyName`
FROM beauty b
LEFT JOIN boys bo
ON b.`boyfriend_id`= bo.`id`
WHERE bo.`id` IS NULL;
# 查询没有男朋友的女神名(右外连接,结果与上同)
SELECT b.`name`,bo.`boyName`
FROM boys bo
RIGHT JOIN beauty b
ON b.`boyfriend_id`= bo.`id`
WHERE bo.`id` IS NULL;
# 查询那个部门没有员工(左外连接)
SELECT `department_name`,`employee_id`
FROM departments d
LEFT JOIN employees e
ON d.`department_id`=e.`department_id`
WHERE e.`employee_id` IS NULL;
# 查询那个部门没有员工(右外连接)
SELECT `department_name`,`employee_id`
FROM employees e
RIGHT JOIN departments d
ON d.`department_id`=e.`department_id`
WHERE e.`employee_id` IS NULL;
/*左右外连接:主表数据到子表数据的映射,没有匹配的数据为null
全外连接:不分主子表,互相映射,没有匹配的数据为null */
SELECT b.*,bo.* # full join
FROM beauty b
FULL JOIN boys bo
ON b.`boyfriend_id`=bo.id; # 代码无法执行,掌握原理即可
# 交叉连接(笛卡尔乘积) cross join
SELECT b.*,bo.*
FROM beauty b
CROSS JOIN boys bo;# 表1行数与表2行数相乘,每一条数据都笛卡尔分配
#---------------------------------练习练习练习-------------------------------------------
# 查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有用null填充
SELECT b.`name`,bo.*
FROM beauty b
LEFT JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
WHERE b.`id` > 3;
# 查询哪些城市没有部门
SELECT `city`
FROM departments d
RIGHT JOIN locations l
ON d.`location_id`=l.`location_id`
WHERE d.`department_id` IS NULL;
# 查询部门名为SAL或IT的员工信息
SELECT d.`department_name` 部门名,e.*
FROM employees e
RIGHT JOIN departments d
ON e.`department_id`=d.`department_id` # 内连接不分主子表,外连接要分主子表
WHERE d.`department_name` IN('SAL','IT'); # 可用内连接,查看效果
#---------------------------------------------------------------------------------------
五、子查询
/* 子查询
含义:出现在其他语句中的select语句,称为子查询或内查询
外部的查询语句,称为主查询或外查询
分类:
按子查询出现的位置:
select后面(仅支持标量子查询)
from后面(支持表子查询)
where或having后面(支持标量、列、行子查询)☆
exists后面(相关表子查询)
按结果集的行列数不同:
标量子查询(结果集只有一行一列)
列子查询(结果集只有一列多行)
行子查询(结果集有一行多列)
表子查询(结果集一般为多行多列)*/
/* 一、where或having后面
特点:
①子查询放在小括号内
②子查询一般放在条件右侧
③标量子查询,一般搭配着单行操作符使用(单行操作符:>,<,=,<>)
④列子查询,一般搭配着多行操作符使用(多行操作符:in,any,some,all)
⑤子查询比主查询先运行 */
# 1、标量子查询(单行子查询)
# 查询谁的工资比Abel高?
SELECT `last_name`
FROM employees
WHERE salary > (SELECT salary FROM employees WHERE last_name='Abel');
# 返回`job_id`与141号员工相同,且`salary`比143号员工多的员工姓名、`job_id`和工资
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);
# 返回公司工资最少的员工的`last_name`,`job_id`和`salary`
SELECT `last_name`,`job_id`,`salary`
FROM employees
WHERE `salary`=(SELECT MIN(`salary`) FROM employees);
# 查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT `department_id`,MIN(`salary`)
FROM employees
GROUP BY `department_id`
HAVING MIN(`salary`) > (SELECT MIN(`salary`) FROM employees WHERE `department_id`=50);
# 2、列子查询(多行子查询)
# 返回`location_id`是1400或1700的部门中的所有员工姓名
SELECT `last_name`
FROM employees
WHERE `department_id` IN(SELECT `department_id` FROM departments WHERE `location_id` IN(1400,1700));
# 返回其它工种中比`job_id`为‘IT_PROG’部门任一工资低的员工的员工工号、姓名、`job_id`和salary
# ① 查询`job_id`为'IT_PROG'部门任一工资
SELECT DISTINCT(`salary`)
FROM employees
WHERE `job_id` = 'IT_PROG';
# ② 查询比①中工资低的员工工号、姓名、`job_id`和salary
SELECT `employee_id`,`last_name`,`job_id`,`salary`
FROM employees
WHERE salary < ANY(SELECT DISTINCT(`salary`)
FROM employees
WHERE `job_id` = 'IT_PROG')
AND `job_id` <> 'IT_PROG';
# 返回其它工种中比`job_id`为‘IT_PROG’部门所有工资低的员工的员工工号、姓名、`job_id`和salary
SELECT `employee_id`,`last_name`,`job_id`,`salary`
FROM employees
WHERE salary < ALL(SELECT DISTINCT(`salary`)
FROM employees
WHERE `job_id` = 'IT_PROG')
AND `job_id` <> 'IT_PROG';
# 3、行子查询(一行多列)
# 查询员工编号最小并且工资最高的员工信息
SELECT * #(标量子查询)
FROM employees
WHERE `employee_id` = (SELECT MIN(`employee_id`) FROM employees)
AND `salary` = (SELECT MAX(`salary`) FROM employees);
# 或
SELECT *
FROM employees
WHERE (`employee_id`,`salary`) = (SELECT MIN(`employee_id`),MAX(`salary`) FROM employees);
# 二、放select后面 (仅支持标量子查询)
# 案例:查询每个部门的员工个数
SELECT d.*,(SELECT COUNT(*) FROM employees e WHERE e.`department_id`=d.`department_id`) AS 个数
FROM departments d;
# 查询员工号=102的部门名
SELECT `department_name`
FROM departments d,employees e
WHERE d.`department_id`=e.`department_id` AND e.`employee_id`=102;
# 或
SELECT (SELECT `department_name`
FROM departments d
INNER JOIN employees e
ON d.`department_id`=e.`department_id`
WHERE `employee_id`=102) 部门名;
# 三、放from后面
# 查询每个部门的平均工资的工资等级
# ① 每个部门的平均工资
SELECT AVG(`salary`)
FROM employees
GROUP BY `department_id`;
# ② 连接①的结果集和`job_grades`表,筛选平均工资在`lowest_sal`和`highest_sal`之间
SELECT g.`grade_level`
FROM job_grades g
INNER JOIN (SELECT AVG(`salary`) ag # 查询的结果集当成一张表用
FROM employees # (必须起别名)
GROUP BY `department_id`) ag_dep
ON ag_dep.ag BETWEEN g.`lowest_sal` AND g.`highest_sal`;
# 四、放exists后面(相关子查询)
/* exists判断是还是否,有还是无,布尔类型(1代表True,0代表False)
语法:exists(完整的查询语句) 结果:1或0 */
SELECT EXISTS(SELECT `employee_id` FROM employees);#括号中不管几行,只要能返回就为True
SELECT EXISTS(SELECT `employee_id` FROM employees WHERE salary=0);#无返回为False
# 查询有员工名的部门名
SELECT `department_name`
FROM departments d
WHERE EXISTS( SELECT *
FROM employees e
WHERE d.`department_id`=e.`department_id`);
# 或用in
SELECT `department_name`
FROM departments d
WHERE d.`department_id` IN( SELECT `department_id`
FROM employees e
WHERE e.`department_id`=d.`department_id`);
# 查询没有女朋友的男神信息
# 用in
SELECT bo.*
FROM boys bo
WHERE bo.`id` NOT IN( SELECT `boyfriend_id` FROM beauty);
# 用exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS( SELECT `boyfriend_id`
FROM beauty b
WHERE b.`boyfriend_id`=bo.`id`);
#----------------------------------------练习练习练习----------------------------------
# 查询和Zlotkey相同的部门的员工姓名和工资
SELECT `last_name`,`salary`
FROM employees
WHERE `department_id`=( SELECT `department_id`
FROM employees
WHERE `last_name`='Zlotkey');
# 查询工资比公司平均工资高的员工的员工号、姓名和工资
SELECT `employee_id`,`last_name`,`salary`
FROM employees
WHERE `salary` > (SELECT AVG(`salary`)
FROM employees);
# 查询各部门中工资比本部门平均工资高的员工工号、姓名和工资☆
# ①查询各部门的平均工资
SELECT AVG(salary)
FROM employees
GROUP BY `department_id`;
# ②连接①和`employees`表,进行筛选
SELECT `employee_id`,`last_name`,`salary`
FROM employees e
INNER JOIN ( SELECT AVG(salary) ag,`department_id`
FROM employees
GROUP BY `department_id`) ag_dep
ON e.`department_id`=ag_dep.`department_id`
WHERE `salary`>ag_dep.ag;
# 查询和姓名中包含字母U的员工在相同部门员工的员工号和姓名
SELECT `employee_id`,`last_name`
FROM employees
WHERE `department_id` IN(SELECT `department_id`
FROM employees
WHERE `last_name` LIKE '%u%');
# 查询在部门的`location_id`为1700的部门工作的员工的员工号
SELECT `employee_id`
FROM employees e
WHERE `department_id` IN(SELECT `department_id`
FROM departments d
WHERE `location_id`=1700);
# 查询管理者是K_ing的员工姓名和工资
SELECT `last_name`,`salary`
FROM employees
WHERE `manager_id` IN( SELECT `employee_id`
FROM employees
WHERE last_name = 'K_ing');
# 查询工资最高的员工的姓名,要求`first_name`和`last_name`显示为一列
SELECT MAX(salary)
FROM employees;
#
SELECT CONCAT(`first_name`,`last_name`) '姓.名'
FROM employees
WHERE salary IN(SELECT MAX(salary)
FROM employees);
#---------------------------------------------------------------------------------------
六、分页和联合查询
/* 分页查询(limit)
应用:当要显示的数据一页显示不全,需要分页提交sql请求
语法:
select 查询列表
from 表
【join 表2
on 连接条件
where...
group by
order by】
limit offset,size; 【】内为选择性添加
offset要显示条目的起始索引(起始索引从0开始)
size 要显示的条目个数
特点:limit语句放在最后 */
# 查询前五条员工信息
SELECT *
FROM employees
LIMIT 0,5;
# 或 (若从第一条开始可以省略起始索引)
SELECT *
FROM employees
LIMIT 5;
# 查询第11条到第25条员工信息
SELECT *
FROM employees
LIMIT 10,15;
# 有奖金的员工信息,且工资较高的前10名显示出来
SELECT *
FROM employees
WHERE `commission_pct` IS NOT NULL
ORDER BY `salary` DESC
LIMIT 10;
# 查询工资最低的员工的`last_name`和`salary`
SELECT `last_name`,`salary`
FROM employees
WHERE salary=(SELECT MIN(`salary`) FROM employees);
# 查询平均工资最低的部门 ☆
# ①求各部门的平均工资
SELECT AVG(`salary`)
FROM employees
GROUP BY `department_id`;
# ②查询①结果上的最低平均工资
SELECT MIN(ag)
FROM (SELECT AVG(`salary`) ag,`department_id`
FROM employees
GROUP BY `department_id`) ag_dep;
# ③查询哪个部门平均工资=②
SELECT AVG(`salary`),`department_id`
FROM employees
GROUP BY `department_id`
HAVING AVG(`salary`) =(SELECT MIN(ag)
FROM (SELECT AVG(`salary`) ag,`department_id`
FROM employees
GROUP BY `department_id`) ag_dep);
# 或
SELECT AVG(`salary`),`department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary)
LIMIT 0,1;
# 查询平均工资最低的部门信息和该部门的平均工资
# ①求平均工资最低的部门编号
SELECT AVG(`salary`),`department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary)
LIMIT 0,1;
# ②查询部门信息
SELECT d.*,ag
FROM departments d
JOIN (SELECT AVG(`salary`) ag,`department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary)
LIMIT 0,1) ag_dep
ON d.`department_id`=ag_dep.`department_id`;
# 查询平均工资最高的jobs表信息
# ①查询每个job的平均工资
SELECT AVG(salary)
FROM employees
GROUP BY `job_id`;
# ②找①中平均工资最高的
SELECT AVG(salary),`job_id`
FROM employees
GROUP BY `job_id`
ORDER BY AVG(salary) DESC
LIMIT 1;
# ③查询jobs表信息
SELECT *
FROM jobs
WHERE job_id=(SELECT `job_id`
FROM employees
GROUP BY `job_id`
ORDER BY AVG(salary) DESC
LIMIT 1);
# 查询平均工资高于公司平均工资的部门有哪些
# ①查公司平均工资
SELECT AVG(salary)
FROM employees;
# ②查各部门的平均工资
SELECT AVG(salary)
FROM employees
GROUP BY `department_id`;
# ③筛选②结果集,满足平均工资>①
SELECT AVG(salary),`department_id`
FROM employees
GROUP BY `department_id`
HAVING AVG(salary)>(SELECT AVG(salary)
FROM employees);
# 查询出公司所有manager的详细信息
# ①查询所有manager的员工编号
SELECT DISTINCT(`manager_id`)
FROM employees;
# ②查询员工编号=①的信息
SELECT *
FROM employees
WHERE `employee_id` IN(SELECT DISTINCT(`manager_id`)
FROM employees);
# 各个部门中最高工资中最低的那个部门的最低工资是多少
# ①各个部门的最高工资中最低的那个部门
SELECT `department_id`
FROM employees
GROUP BY `department_id`
ORDER BY MAX(salary) ASC
LIMIT 1;
# ②查询对应①部门的最低工资
SELECT MIN(`salary`)
FROM employees
WHERE `department_id` =(SELECT `department_id`
FROM employees
GROUP BY `department_id`
ORDER BY MAX(salary) ASC
LIMIT 1);
# 查询平均工资最高的部门的manager的`last_name`,`department_id`,`email`,`salary`
# ①平均工资最高的部门
SELECT `department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary) DESC
LIMIT 1;
# ②查询部门id等于①的manager_id
SELECT `manager_id`
FROM employees
WHERE `department_id` = (SELECT `department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary) DESC
LIMIT 1);
# ③查询manager_id=②的`last_name`,`department_id`,`email`,`salary`
SELECT `last_name`,`department_id`,`email`,`salary`
FROM employees
WHERE `manager_id` IN(SELECT `manager_id`
FROM employees
WHERE `department_id` = (SELECT `department_id`
FROM employees
GROUP BY `department_id`
ORDER BY AVG(salary) DESC
LIMIT 1));
#---------------------------------------------------------------------------------------
/* 联合查询
union 联合 合并:将多条查询语句的结果合并成一个结果
应用:可以应用于多个没有联系的表,但查询的信息要一致
特点:1、要求多条查询语句的列数一致
2、要求多条查询中每一列的类型和顺序一致
3、union默认去重,使用union all可以包含重复*/
# 查询部门编号>90或邮箱中包含a的员工信息(以前方法)
SELECT * FROM employees WHERE `department_id` > 90 OR `email` LIKE '%a%';
# 或 (联合查询)
SELECT * FROM employees WHERE `department_id` > 90
UNION
SELECT * FROM employees WHERE `email` LIKE '%a%';
#---------------------------------------------------------------------------------------
七、数据的增、改、删
# DML语言(数据操作语言)
/*
插入:insert into ...
修改:update ... set ...
删除:delete from...
truncate table ... */
# 一、插入语句
/* 语法:
insert into 表名(列名,...)
values(值1,...);
*/
# 1、插入的值类型要与列的类型一致或兼容
INSERT INTO beauty(`id`,`name`,`sex`,`borndate`,`phone`,`photo`,`boyfriend_id`)
VALUES(13,'唐艺昕','女','1990-4-23','18988888888',NULL,2);
# 2、不可以为null的列必须插入值,可以为null的列如何插入值?
# 方式一:同上
INSERT INTO beauty(`id`,`name`,`sex`,`borndate`,`phone`,`photo`,`boyfriend_id`)
VALUES(13,'唐艺昕','女','1990-4-23','18988888888',NULL,2);
# 方式二:
INSERT INTO beauty(`id`,`name`,`sex`,`phone`)
VALUES(15,'娜扎','女','18988888888');
# 3、列的顺序可以调换
INSERT INTO beauty(`name`,`sex`,`id`,`phone`)
VALUES('蒋欣','女',16,'110');
# 4、列数和值的个数要一致
INSERT INTO beauty(`name`,`sex`,`id`,`phone`,`boyfriend_id`)
VALUES('关晓彤','女',17,'110'); # 不一致,报错
# 5、可以省略列名,默认所有列,而且列的顺序和表中一致
INSERT INTO beauty
VALUES(18,'张飞','男',NULL,'119',NULL,NULL);
/* 插入语句另一种方式
语法:
insert into 表名
set 列名=值,列名=值,...; */
INSERT INTO beauty
SET `id`=19,`sex`='女',`name`='刘涛',`phone`=999;
# 两种方式大pk
# 1、第一种方法支持插入多行,第二种方法不支持
INSERT INTO beauty
VALUES(13,'唐艺昕','女','1990-4-23','18988888888',NULL,2),
(14,'杨幂','女','1990-5-26','18988888882',NULL,2),
(15,'赵丽颖','女','1990-6-4','18988888884',NULL,2);
# 2、第一种方法支持子查询,第二种方法不支持
INSERT INTO beauty(`id`,`name`,`phone`)
SELECT 26,'宋茜','11809866'; # 可以用select 子查询(不用values)
# 二、修改语句
/*
1、修改单表记录
语法: update 表名
set 列=新值,列=新值,...(要修改内容)
where 筛选条件;
2、修改多表记录
sql92语法:
语法: update 表1 别名,表2 别名
set 列=新值,...(要修改内容)
where 连接条件
and 筛选条件;
sql99语法:
语法: update 表1 别名
inner|left|right join 表2 别名
on 连接条件
set 列=新值,...(要修改内容)
where 筛选条件
*/
# 1、修改单表
# 修改beauty表中姓唐的女神电话为13899888899
UPDATE beauty
SET `phone`=13899888899
WHERE NAME LIKE '唐%';
# 将boys表中id=2的男神名改成张飞,魅力值为10
UPDATE boys
SET `boyName` = '张飞',`userCP` = 10
WHERE id = 2;
# 2、修改多表
# 修改张无忌的女朋友的手机号为114
UPDATE `beauty` b
LEFT JOIN boys bo #注意之前的是select...from(表1)join(表2)on(连接条件)
ON bo.`id`=b.`boyfriend_id`
SET b.`phone` = 114
WHERE bo.`boyName` = '张无忌';
# 修改没有男朋友的女神的男朋友编号都为2号(有误)
UPDATE boys bo
RIGHT JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
SET b.`boyfriend_id`=2
WHERE b.`boyfriend_id` IS NULL;
# 三、删除语句
/*
方式一:delete (删整行)
1、单表的删除
语法:delete from 表名 where 筛选条件
2、多表的删除
语法:delete 表1的别名,表2的别名
from 表1 别名
inner|left|right join 表2 别名
on 连接条件
where 筛选条件;
方式二:truncate(删全表)
语法:truncate table 表名; */
# 方式一:delete
# 1、单表删除
# 删除手机号以9结尾的女神信息
DELETE FROM `beauty` WHERE `phone` LIKE '%9';
# 2、多表删除
# 删除张无忌的女朋友信息
DELETE b
FROM beauty b
INNER JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
WHERE bo.`boyName`='张无忌';
# 删除黄晓明和他女朋友的信息
DELETE b,bo
FROM beauty b
INNER JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
WHERE bo.`boyName`='黄晓明';
# 方式二:truncate (不能加where)
TRUNCATE TABLE `boys`
# 将魅力值>100的男神信息删除
TRUNCATE TABLE `boys` WHERE `userCP`>100; #错误,不能加where
# delete和truncate区别:
/* 假如要删除的表中有自增长列,
如果用delete删除后,再插入数据,自增长列的值从断点开始,
而truncate删除后,再插入数据,自增长列的值从1开始。
(自增长:增加数据行,它的id自动紧随之前的id加1)*/
# delete删除有返回值,truncate删除没有返回值
# truncate删除不能回滚,delete删除可以回滚
# delete可加where,truncate不能加where
/*
增:insert into 表 values();
改:单表:update 表 set 条件;
多表:update 表1,表2 from 表1 join 表2 on 连接条件 set 条件 where ...;
删:行删:delete from 表 where ...;
全删:truncate table 表;
*/
#---------------------------------------------------------------------------------------
八、库和表的管理
# DDL
/*
数据定义语言,库和表的管理
一、库的管理
创建、修改、删除
二、表的管理
创建、修改、删除
对库和表的操作: 对表中数据的操作:
创建:create 增:insert
修改:alter 改:update
删除:drop 删:delete
*/
# 一、库的管理
/* 1、库的创建
语法:
create database [if not exists]库名;
*/# 创建库book
CREATE DATABASE books;
CREATE DATABASE IF NOT EXISTS books; # 如果不存在则创建
# 2、库的修改
# 改库名到文件夹中改
# 改库的编码字符集 (alter database 库名 character set 编码)
ALTER DATABASE books CHARACTER SET utf8;
# 3、库的删除
DROP DATABASE books;
DROP DATABASE IF EXISTS books; #如果存在则删除
# 二、表的管理
# 1、表的创建
/*语法:
create table [if not exists]表名(
列名,列类型【(长度) 约束】,
列名,列类型【(长度) 约束】,
列名,列类型【(长度) 约束】,
...)
*/
# 创建表book
CREATE TABLE book(id INT,#整数
bname VARCHAR(20),#字符型,最大长度为20
place DOUBLE,
authorID INT,
publishDate DATETIME); #日期
CREATE TABLE author(id INT,
au_name VARCHAR(20),
nation VARCHAR(20));
# 2、表的修改(改列名、改列类型或约束、添加列、删除列、改表名)
/* alter table 表名 change column|modify column|
add column|drop column|rename to 列名 【列类型 约束】; */
# 改列名
ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME;
# 改列类型
ALTER TABLE book MODIFY COLUMN pubDate TIMESTAMP;
# 添加列
ALTER TABLE author ADD COLUMN annual DOUBLE;
# 删除列
ALTER TABLE author DROP COLUMN annual;
# 改表名
ALTER TABLE author RENAME TO book_author;
# 3、表的删除
DROP TABLE book_author;
DROP TABLE IF EXISTS book_author;
# 4、表的复制
INSERT INTO book_author
VALUES (1,'村上春树','日本'),
(2,'莫言','中国'),
(3,'冯唐','中国'),
(4,'金庸','中国'); # 给表添加数据
# 仅仅复制表的结构
CREATE TABLE copy LIKE book_author;
# 复制表的结构+全部内容
CREATE TABLE copy2 SELECT * FROM book_author;
# 复制表的结构+部分内容
CREATE TABLE copy3 SELECT `id`,`au_name` FROM `book_author` WHERE nation='中国';
# 仅仅复制某些字段(索引)
CREATE TABLE copy4 SELECT `id`,`au_name` FROM `book_author` WHERE 1=2;# 不成立就没有数据
#--------------------------------------练习练习练习------------------------------------------
# 创建库test
CREATE DATABASE IF NOT EXISTS test;
# 创建表dept1
CREATE TABLE dept1(id INT(7),
NAME VARCHAR(25));
# 将表departments中的数据插入新表dept2中(可以跨库)
CREATE TABLE dept2
SELECT `department_id`,`department_name`
FROM `myemployees`.departments;
# 创建表emp5
CREATE TABLE emp5(id INT(7),
First_name VARCHAR(25),
Last_name VARCHAR(25),
Dept_id INT(7));
# 将列Last_name的长度增加到50
ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);
# 根据表`employees`创建employees2(复制)
CREATE TABLE employees2 SELECT * FROM `myemployees`.`employees`;
# 删除表emp5
DELETE TABLE IF EXISTS emp5;
# 将表employees2重命名为emp5
ALTER TABLE `myemployees`.employees2 RENAME TO emp5;
# 在表dept和emp5中添加新列test_column,并检查所作的操作
ALTER TABLE dept ADD COLUMN IF NOT EXISTS test_column INT;
ALTER TABLE emp5 ADD COLUMN IF NOT EXISTS test_column INT;
# 直接删除表emp5中的列dept_id
ALTER TABLE emp5 DROP COLUMN dept_id;
#---------------------------------------------------------------------------------------
九、数据类型
# 常见的数据类型(实际应用中所选类型越简单越好)
/*
数值型:
整型
小数
定点数
浮点数
字符型:
较短的文本:char、varchar
较长的文本:text、blob(图片)
日期型:
*/
# 一、整型
/* Tinyint 微整型 1个字节
Smallint 小整型 2个字节
Mediumint 中整型 3个字节
Int 常用整型 4个字节
Bigint 大整型 8个字节
特点:
①如果不设置有无符号,默认为有,否则追加unsigned
②如果插入数值超过整型范围,只插入到临界值
③如果不设置长度,会有默认长度
(长度就是int()括号内数值,它代表了显示的最大宽度,如果不够可使用zerofill用0左填充)
*/
# 1、如何设置无符号和有符号(无符号不能插入负数,有符号可以)
CREATE TABLE tab_int(t1 INT,
t2 INT UNSIGNED); # unsigned无符号
INSERT INTO tab_int VALUES(-5,5); #由于无符号,t2不可插入负数
DESC tab_int; #查询表信息
# 2、长度设置
CREATE TABLE tab_int2(t1 INT,
t2 INT(7) ZEROFILL); # zerofill,用0填充长度,无符号
INSERT INTO tab_int2 VALUES(-5,5); #由于无符号,t2不可插入负数
SELECT * FROM tab_int2; # int有7个长度,用0填充
# 二、小数
/*
浮点型:float(M,D) 4个字节
double(M,D) 8个字节
定点型:Dec(M,D) 与double类似,但是精度更高
特点:
①M表示整数和小数位数一共的长度(小数点算一位)
②D表示四舍五入保留几位小数
③M和D可以不设置,会有默认设置(10,0)
*/
# 测试M和D
CREATE TABLE tab_float(f1 FLOAT(5,2),
f2 DOUBLE(5,2),
f3 DEC(5,2));
INSERT INTO tab_float VALUES(1523.45,123.45,123.45); # 都插入123.45
INSERT INTO tab_float VALUES(1523.456,123.456,123.456); # 插入123.456
INSERT INTO tab_float VALUES(1523.45,1523.45,1523.45); # 都插入1523.45 错误
# 三、字符型
/*
较短的文本:char(M) 固定字符 耗空间 效率高
varchar(M) 可变字符 较节省空间 效率低
enum(列表) 用于保存枚举
set(列表) 用于保存集合
binary和varbinary 用于保存较短的二进制
较长的文本:text
blob(保存图片)
特点:
char的M可以省略,默认为1
varchar的M不可以省略
*/
# enum类型
CREATE TABLE tab_char(cl 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('A'); # 不区分大小写
INSERT INTO tab_char VALUES('m'); # 插入enum中不存在的m 错误
# set类型
CREATE TABLE tab_set(sl SET('a','b','c'));
INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('A,b,c'); # 可多个一起插入,不区分大小写
INSERT INTO tab_set VALUES('A,a,c'); # 有重复只保留一个,这里a重复,插入为(a,c)
# 四、日期型
/*
date 4个字节 只有日期
datetime 8个字节 日期+时间
timestamp 4个字节 日期+有时间
time 3个字节 只有时间
year 1个字节 只有年
datetime和timestamp的区别:
①datetime的范围大1000-9999,timestamp的范围小1970-2038
②datetime只能反映插入时当地时区
timestamp和实际时区有关,更能反映实际的日期
③timestamp的属性受Mysql版本和SQLMode的影响很大
*/
CREATE TABLE tab_date(t1 DATETIME,
t2 TIMESTAMP);
INSERT INTO tab_date VALUES(NOW(),NOW());# 北京时间,东八区
SHOW VARIABLES LIKE 'time_zone'; #其中value值为系统时区
SET time_zone = '+9:00'; # 更改时区为东9区
#---------------------------------------------------------------------------------------
十、常见约束
# 常见约束
/*
含义:一种限制,用于限制表中的数据,为了保证表中数据的准确和可靠性。
分类:六大约束
NOT NULL:非空约束,保证该字段的值不能为空(如姓名、学号)
default:默认,用于保证该字段有默认值(如性别,没设置时可默认为男)
primary key:主键(一般只有一个主键,比如id是表的主键),用于保证该字段的值具有唯一性,且非空(如学号都不同,有唯一性)
unique:唯一,用于保证该字段的值具有唯一性,可以为空
check:检查约束【mysql不支持】(比如年龄,定义在18-60岁)
foreign key:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值
在从表添加外键约束,用于引用主表中某列的值(如学生表的专业编号,员工表的工种编号)
添加约束的时机:
1、创建表时
2、修改表时
(都在数据被插入之前)
约束的添加分类:
列级约束:create table 表名(字段名 字段类型 约束);
(六大约束语法上都支持,但外键约束没有效果)
表级约束:create table 表名(字段名 字段类型 约束,
字段名 字段类型 约束,
【constraint 约束名】 约束类型(字段名)
【constraint 约束名】 约束类型(字段名) references 外表(字段名)); #将本表某列和外表某列连接
(除了非空、默认,其它约束都支持)
约束可以同时添加多个:
如 create table if not exists syuinfo(id int primary key,
stuname varchar(20) not null unique);
(加not null约束后再加unique约束)
*/
# 一、创建表时添加约束
CREATE DATABASE students; # 创建新库
USE students;
# 1、添加列级约束
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,/* 默认约束为18岁*/
majorID INT REFERENCES major(id));#外键,majorID与major表id列连接
CREATE TABLE major(id INT PRIMARY KEY,
majorName VARCHAR(20));
SHOW INDEX FROM stuinfo;# 查看索引,显示包括主键、外键、唯一
# 2、添加表级约束
/*
语法:在各个字段的最下面
【constraint 约束名】 约束类型(字段名)
*/
CREATE TABLE stuinfo2(id INT,
stuName VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorID INT,
CONSTRAINT pk PRIMARY KEY(id),#主键
CONSTRAINT ug UNIQUE(seat),# 唯一键,可多个,如unique(seat,seat2)
CONSTRAINT ck CHECK(gender='男' OR gender='女'),# 检查
CONSTRAINT fk_stuinfo2_major FOREIGN KEY(majorid) REFERENCES major(id));#外键,将fk_stuinfo2_major列与major的id列相连接
SHOW INDEX FROM stuinfo2;# 查看索引,显示包括主键、外键、唯一
/* 通用写法:
create table if not exists syuinfo(id int primary key,
stuname varchar(20) not null,
sex char(1),
age int default 18,
seat int unique,
majorid int,
constraint fk_stuinfo_major foreign key(majorid) references major(id));
*/
/* 外键:
1、要求在从表设置外键关系
2、从表的外键列类型和主表的关联列的类型要求一致或兼容,名称无要求
(如上面的syuinfo表的majorid列是int类型,major表的id列也是int类型)
3、主表的关联列必须是一个key(一般是主键或唯一键)
4、插入数据时,先插入主表,再插入从表
5、删表先删从表,建表先建主表 */
# 二、修改表时添加约束
/*
1、添加列级约束
alter table 表名 modify column 列名 列类型 新约束;
2、添加表级约束
alter table 表名 add 【constraint 约束名】 新约束(列名);
*/
CREATE TABLE stuinfo3(id INT,
stuName VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorID INT);
# 1、添加非空约束(NOT NULL)
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NOT NULL;
# 2、添加默认约束(default)
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
# 3、添加主键约束(primary key)
# ①列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
# ②表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);
# 4、添加唯一约束(unique)
# ①列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
# ②表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);
# 5、添加外键约束(foreign key)
ALTER TABLE stuinfo ADD FOREIGN KEY(majorId) REFERENCES major(id);
# 三、修改表时删除约束
# 1、删除非空约束(not null)
ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NULL;
# 2、删除默认约束(default)
ALTER TABLE stuinfo MODIFY COLUMN age INT NULL;
# 3、删除主键约束(primary key)
ALTER TABLE stuinfo MODIFY COLUMN id INT NULL;
ALTER TABLE stuinfo DROP PRIMARY KEY;#两种方法
# 4、删除唯一约束(unique)
ALTER TABLE stuinfo MODIFY COLUMN seat INT NULL;
ALTER TABLE stuinfo DROP INDEX seat;#两种方法
# 5、删除外键约束(foreign key)
ALTER TABLE stuinfo MODIFY COLUMN majorId INT NULL;
ALTER TABLE stuinfo DROP FOREIGN KEY majorId;
#-------------------------------------练习练习练习---------------------------------------
CREATE TABLE IF NOT EXISTS emp2(id INT);
# 1、向表emp2的id列中添加primary key约束(称为my_emp_id_pk)
ALTER TABLE emp2 ADD CONSTRAINT my_emp_id_pk PRIMARY KEY(id);
# 2、向表dept2的department_id列中添加primary key约束(称为my_dept_id_pk)
ALTER TABLE dept2 ADD CONSISTENT my_dept_id_pk PRIMARY KEY(department_id);
# 3、向表emp2中添加列dept_id,并在其中定义foreign key约束,与之相关联的列是dept2表中的department_id列
ALTER TABLE emp2 ADD COLUMN dept_id INT;
ALTER TABLE emp2 ADD FOREIGN KEY(dept_id) REFERENCES dept2(department_id);
/*
位置 支持的约束类型 是否可以起约束名
列级约束: 列的后面 语法都支持,外键没有效果 不可以
表级约束: 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
*/
#---------------------------------------------------------------------------------------
十一、标识列
# 标识列
/* 又称为自增长列
含义:可以不用手动插入值,系统提供默认的序列值(步长为1)
特点:1、标识列只能和主键、外键带Key的搭配
2、一张表最多只能有一个标识列
3、只能对数值型设置标识列
4、标识列可以通过SET auto_increment_increment=3设置步长(可手动设置起始值)
*/
# 一、创建表时设置标识列
CREATE TABLE tab_identify(id INT PRIMARY KEY AUTO_INCREMENT,#AUTO_INCREMENT设置标识列
NAME VARCHAR(20),
seat INT);
# 本来重复插入id=1的值会报错,现在插入会自动将id排序(auto_increment增量)
INSERT INTO tab_identify VALUES(NULL,'john');
INSERT INTO tab_identify(NAME) VALUES('lucy');# 或
SHOW VARIABLES LIKE '%auto_increment%';#显示值的详细信息,increment为步长,offset为起始值
# increment步长改为3,上面的id以步长为3自动排序。要改起始值则改最初的values值即可
SET auto_increment_increment=3;
# 二、修改表时设置标识列
CREATE TABLE tab_identify2(id INT,#创建表
NAME VARCHAR(20),
seat INT);
ALTER TABLE tab_identify2 MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;#修改标识列
# 三、修改表时删除标识列
ALTER TABLE tab_identify2 MODIFY COLUMN id INT;#删除标识列
#---------------------------------------------------------------------------------------
十二、事务
# TCL 事务控制语言
/*
事务的含义:
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行
案例:转账(张三丰给郭襄转账500元)
张三丰:1000元
郭襄:1000元
update 表 set 张三丰的余额=500 where name=‘张三丰’; ①
update 表 set 郭襄的余额=1500 where name=‘郭襄’; ②
事务就是让上面的①和②代码同时执行或同时不执行。
(不能第①条执行后产生意外使第②条不执行,前者扣款后者却没收到款)
事务的属性:
1、原子性:要么都发生,要么都不发生
2、一致性:必须使数据库从一个一致性状态变换到另外一个一致性状态
3、隔离性:事务的执行不能被其他事务干扰,各个事务之间也互不干扰
4、持久性:一个事务被提交,它对数据库中数据的改变是永久性的
事务的创建
隐式事务:事务没有明显的开启和结束的标记(如select、update、delete语句等)
显示事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用 set autocommit=0;
开启:
步骤1
set autocommit=0;
start transaction;
步骤2
编写事务中的sql语句(select、insert、delete、update)
语句1;
语句2;
...
步骤3
结束事务
commit;提交事务
rollback;回滚事务
savepoint 节点名; 设置节点/保存点
事务的隔离级别:
read uncommitted; 出现脏读、幻读、不可重复读都可隔离
read committed; 不隔离脏读,隔离幻读和不可重复读(Oracle默认)
repeatable read; 隔离幻读(mysql默认)
serializable 无法隔离
*/
# 查看Mysql的存储引擎,主流的有InnoDB、MEMORY、MyISAM(其中只有InnoDB支持事务)
SHOW ENGINES;
# 查询autocommit信息,其中为on说明原事务已开启
SHOW VARIABLES LIKE 'autocommit';
# set命令设置原事务禁止,其中off说明原事务已禁止
SET autocommit=0;
SHOW VARIABLES LIKE 'autocommit';
# 1、演示事务使用步骤
CREATE TABLE account(id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(25),
balance DOUBLE);
ALTER TABLE account CONVERT TO CHARACTER SET gbk COLLATE gbk_chinese_ci;# 改username列的编码
SHOW FULL COLUMNS FROM account;# 查看编码convert to character set collate
INSERT INTO account(username,balance) VALUES ('张无忌',1000),('赵敏',1000);
# 步骤1 开启事务
SET autocommit=0;
START TRANSACTION;
# 步骤2 编写事务
UPDATE account SET balance = 500 WHERE username = '张无忌';
UPDATE account SET balance = 1500 WHERE username = '赵敏';
# 步骤3 结束事务
COMMIT; #或用回滚rollback
# 2、事务的隔离级别设置
SELECT @@tx_isolation; # 查看隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;# 设置级别为READ UNCOMMITTED(读未提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;# 设置级别为READ COMMITTED(读已提交)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; # 设置数据库系统的全局的隔离级别
# 3、演示savepoint的使用
SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=25;
SAVEPOINT a; # savepoint设置保存点
DELETE FROM account WHERE id=29;
ROLLBACK TO a; #回滚到保存点处
# 4、delete和truncate在事务使用时的区别
# 演示delete(执行回滚,可撤销)
SET autocommit=0
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
# 演示truancate(不执行回滚,全删,不可撤销)
SET autocommit=0
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;
#---------------------------------------------------------------------------------------
十三、视图
# 视图
/*
含义:虚拟的表,和普通表一样使用(临时的)
应用场景:1、多个地方用到同样的查询结果
2、该查询结果使用的sql语句较复杂时
创建视图:create view 视图名 as 查询语句
视图和表的区别:
创建语法的关键字 是否实际占用空间 使用
视图 create view 只保存了sql逻辑 一般不能增删改
表 create table 保存了数据 可以增删改
*/
# 一、创建和使用视图
# 查询姓张的学生名和专业名
SELECT `stuName`,`majorName`
FROM stuinfo s
INNER JOIN major m
ON m.`id`=s.`majorID`
WHERE s.`stuName` LIKE '张%';
# 封装为视图
CREATE VIEW v1
AS
(SELECT `stuName`,`majorName`
FROM stuinfo s
INNER JOIN major m
ON m.`id`=s.`majorID`); # 括号内为视图,起名为v1
# 使用视图
SELECT * FROM v1 WHERE s.`stuName` LIKE '张%';
# 查询姓名中包含a字符的员工名、部门名和工种信息
# 创建视图
CREATE VIEW myv1
AS
(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 j.`job_id`=e.`job_id`);
# 使用视图
SELECT * FROM myv1 WHERE `last_name` LIKE '%a%';
# 查询各部门的平均工资级别
# ①创建视图查看每个部门的平均工资
CREATE VIEW myv2
AS
SELECT AVG(`salary`) ag FROM employees GROUP BY `department_id`;
# ②使用视图
SELECT myv2.`ag`,g.`grade_level`
FROM `job_grades` g JOIN myv2
ON myv2.`ag` BETWEEN g.`lowest_sal` AND g.`highest_sal`;
# 查询平均工资最低的部门信息
# 创建视图
CREATE VIEW myv3
AS
SELECT AVG(`salary`),`department_id`
FROM employees GROUP BY `department_id`
ORDER BY AVG(`salary`)
LIMIT 1;
# 使用视图
SELECT d.* FROM departments d JOIN myv3
ON d.`department_id`=myv3.`department_id`;
# 二、修改视图
/* 方式一:
create or replace view 视图名 as 查询语句;
(若视图存在则修改,否则创建)
*/
SELECT * FROM myv3; # 查看视图
CREATE OR REPLACE VIEW myv3
AS
SELECT AVG(salary),`job_id`
FROM employees
GROUP BY `job_id`;# myv3存在,故修改
/*方式二:
alter view 视图名 as 查询语句;
*/
ALTER VIEW myv3
AS
SELECT * FROM employees;
# 三、删除视图
/*
语法:drop view 视图名,视图名...;
*/
DROP VIEW myv1,myv2,myv3;
# 四、查看视图
DESC myv3;# 或
SHOW CREATE VIEW myv3;
# 五、视图的更新(很少用)
CREATE OR REPLACE VIEW myv4
AS
SELECT `last_name`,`email`
FROM employees;
# 1、插入数据
INSERT INTO myv4 VALUES('张飞','[email protected]');#同时会对原始表更改
# 2、修改数据
UPDATE myv4 SET `last_name`='张无忌' WHERE `last_name`='张飞';#同时会对原始表更改
# 3、删除数据
DELETE FROM myv4 WHERE `last_name`='张无忌';#同时会对原始表更改
/* 具备以下特点的视图不能更新:
包含以下关键字的sql语句:分组函数、distinct、group by、having、union或union all
常量视图
select中包含子查询
join
from一个不能更新的视图
where子句的子查询引用了from子句中的表
*/
#-------------------------------练习练习练习---------------------------------------------
# 创建视图emp_v1,要求出现电话号码以011开头的员工姓名、工资和邮箱
CREATE OR REPLACE VIEW emp_v1
AS
SELECT `last_name`,`salary`,`email`
FROM employees
WHERE `phone_number` LIKE '011%';
# 创建视图emp_v2,要求出现部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW emp_v2
AS
SELECT d.*
FROM departments d
JOIN (SELECT MAX(`salary`) g,`department_id`
FROM employees
GROUP BY `department_id`) ag
ON d.`department_id`=ag.`department_id`
WHERE ag.g>12000;
# 创建book表
CREATE TABLE book (bid INT PRIMARY KEY,
bname VARCHAR(20) NOT NULL UNIQUE,
price FLOAT DEFAULT 10,
btypeID INT,
FOREIGN KEY(btypeID) REFERENCES bookType(id));
# 开启事务,向表中插入一行数据并结束
SET autocommitted=0;
START TRANSACTION;
INSERT INTO book VALUES(1,'小李飞刀',100,1);
COMMENT;# 或rollback;
# 创建视图,出现价格大于100的书名和类型名
CREATE VIEW myv1
AS
SELECT `bname`,`name`
FROM books b
JOIN bookType t
ON b.id=t.id
WHERE price>100;
# 修改视图
ALTER VIEW myv1
AS
SELECT bname,price
FROM books
WHERE price BETWEEN 90 AND 120;
# 删除视图
DROP VIEW myv1;
#---------------------------------------------------------------------------------------
十四、变量
#变量
/*
系统变量:由系统提供,不是自己定义的变量
全局变量 global
作用域:服务器每次启动将为所有的全局变量赋初始值,针对所有连接有效,但重启会恢复
会话变量 session
作用域:仅仅针对当前连接有效
自定义变量:自己定义的变量
用户变量
作用域:仅仅针对当前连接有效
局部变量
作用域:仅仅在定义它的begin end中有效
*/
# 一、系统变量
#1、查看所有系统变量
SHOW GLOBAL VARIABLES; #查询全局变量
SHOW SESSION VARIABLES;#查询会话变量
#2、查看满足条件的系统变量
SHOW GLOBAL VARIABLES LIKE '%char%'; #注意是like不是where
#3、查看指定的某个系统变量的值
#select @@global|session.系统变量名;
SELECT @@global.autocommit;
SELECT @@tx_isolation;
#4、为某个系统变量赋值(跨连接有效)
#(如果是全局级别,则需要加global,如果是会话级别,可不写session,默认为session)
#方式一:set global|session 系统变量名=值;
SET autocommit=0;
#方式二:set @@global|session.系统变量名=值;
SET @@global.autocommit=0;
#二、自定义变量
/* 用户变量
使用步骤:
①声明: set @用户变量名=值;
set @用户变量名:=值;
select @用户变量名:=值;(以上三种任意一种都可以)
②赋值: set @用户变量名=值;
set @用户变量名:=值;
select @用户变量名:=值;(与上同)
select 字段 into @变量名 from 表名;
③使用(查看、比较、运算等):
select @用户变量名;
应用在任何地方,也就是begin end里面或begin end外面
*/
#1、用户变量
#①声明并初始化
SET @name='john';
SET @name=100;
SET @count=1;
#②赋值
SELECT COUNT(*) INTO @count FROM employees;
#③使用
SELECT @count;
/* 局部变量
使用步骤:
①声明: declare 局部变量名 类型;
declare 局部变量名 类型 default 值;(值类型与类型相同)
②赋值: set 局部变量名=值;
set 局部变量名:=值;
select @局部变量名:=值;(与上同)
select 字段 into @变量名 from 表名;
③使用(查看、比较、运算等):
select 局部变量名;
应用在begin end中的第一句话!!!
*/
# 声明两个变量并赋初始值,求和并打印
# 1、用户变量方式
SET @m=1;
SET @n=2;
SET @sum=@m+@n;
SELECT @sum;
# 2、用局部变量方式(declare 宣布)
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE SUM INT;
SET SUM = m+n;
SELECT SUM;# 代码报错,原因是没有放在begin end中
# 声明两个变量,并拼接打印出来
SET @m='w';
SET @n='d';
SET @sum=CONCAT(@n,@m);
SELECT @sum;
#--------------------------------------------------------------------------------
十五、存储过程
# 存储过程(相当于自己创建的方法)
/*好处:
1、提高代码的重用性
2、简化操作
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高效率
存储过程:一组预先编译好的SQL语句的集合、理解成批处理语句
*/
/*一、创建语法
create procedure 存储过程名(参数列表)
begin
存储过程体,一组合法的SQL语句
end;
注意:
1、参数列表包括三部分:参数模式 参数名 参数类型
比如: in stuname varchar(20)
参数模式:
in:该参数可以作为输入,也就是说该参数需要调用方法传入值
out:该参数可以作为输出,也就是可以作为返回值
inout:该参数即可以作为输入也可以作为输出
2、如果存储过程体仅仅只有一句话,begin end可以省略
存储过程体中每条SQL语句的结尾要求必须加分号;
存储过程的结尾可以使用delimiter重新设置
语法:delimiter 结束标记
如:delimiter $
*/
#二、调用语法
#CALL 存储过程名(实参列表)
# 1、参数列表为空
# 插入到admin表中五条记录(在cmd运行)
DELIMITER $ #设置结束标记(不再用分号结束)
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username,PASSWORD) VALUES('john',0000),
('lily',0000),
('rose',0000),
('jack',0000),
('tom',0000);
END $
# 调用
CALL myp1()$;
# 查看
SELECT * FROM admin $
# 2、创建带in模式参数的存储过程
# 创建存储过程,实现:根据女神名,查询对应的男神信息
DELIMITER $
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
SELECT bo.*
FROM boys bo JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
WHERE b.name=beautyName;
END $
# 调用
SET NAMES gbk $ #更改编码为gbk
CALL myp2('柳岩') $
# 创建存储过程实现:用户是否登录成功(多参数,两个in)
DELIMITER $
CREATE PROCEDURE myp3(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
DECLARE result INT DEFAULT 0;#声明变量result(详细见上一章节)
SELECT COUNT(*) INTO result#赋值
FROM admin a
WHERE a.username=username
AND a.password=PASSWORD;
SELECT IF(result>0,'成功','失败');#使用
END $
# 调用
CALL myp3('john','0000')$
# 3、创建带out模式参数的存储过程
# 根据(in)女神名,返回(out)对应的男神名
CREATE PROCEDURE myp5(IN beautyName VARCHAR(20), OUT boyname VARCHAR(20))
BEGIN
SELECT bo.`boyName` INTO boyname
FROM boys bo JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
WHERE b.`name`=beautyName;
END $
# 调用
SET @bName$#变量声明
CALL myp5('小昭',@bName)$
SELECT @bName$#查看
# 根据(in)女神名,返回对应的男神名和魅力值(多参数,两个out)
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 bo.`id`=b.`boyfriend_id`
WHERE b.`name`=beautyname;#in在where后连接参数,out在select后连接参数
END $
# 调用
SET @bName$
SET @userCP$
CALL myp6('小昭',@bName,@userCP)$
SELECT @bName,@userCP$ #查看结果
# 4、创建带inout模式参数的存储过程
# 传入a和b两个值,最终a和b都翻倍并返回
CREATE PROCEDURE myp8(INOUT a INT,
INOUT b INT)
BEGIN
SET a=a*2;
SET b=b*2;
END $
# 调用
SET @m=10$
SET @n=20$
CALL myp8(@m,@n)$
SELECT @m,@n$ #查看结果
# 三、删除语法(一次只能删一个)
/* 语法:drop procedure 存储过程名; */
DROP PROCEDURE myp1;
DROP PROCEDURE myp1,myp2; # 错误,不能多个同时删除
# 四、查看存储过程的信息
/* 语法:show create procedure 存储过程名; */
SHOW CREATE PROCEDURE myp3;
#-----------------------------------------练习练习练习-----------------------------------
# 1、创建存储过程实现传入用户名和密码,插入到admin表中
CREATE PROCEDURE test_pro1(IN username VARCHAR(20),IN loginPwd VARCHAR(20))
BEGIN
INSERT INTO admin(admin.`username`,`password`) VALUES(username,loginPwd);
END $
# 调用
CALL test_pro1('john','0000')$
# 查看结果
SELECT * FROM admin$
# 2、创建存储过程实现传入女神编号,返回女神名和女神电话
CREATE PROCEDURE test_pro2(IN beautyID INT,
OUT beautyName VARCHAR(50),
OUT bphone VARCHAR(20))
BEGIN
SELECT b.`name`,b.`phone` INTO beautyName,bphone
FROM beauty b
WHERE b.id=beautyID;
END $
# 调用
CALL test_pro2(1,@m,@n)$
# 查看结果
SELECT @m,@n$
# 3、创建存储过程实现传入两个女神生日,返回大小
CREATE PROCEDURE test_pro3(IN beauty_date1 DATETIME,
IN beauty_date2 DATETIME,
OUT result INT)
BEGIN
SELECT DATEDIFF(beauty_date2,beauty_date1) INTO result;
END $
# 调用
CALL test_pro3('1998-1-1',NOW(),@m)$
# 查看结果
SELECT @m $
# 4、创建存储过程实现传入一个日期,格式化成xx年xx月xx日并返回
CREATE PROCEDURE test_pro4(IN mydate DATETIME,OUT strDate VARCHAR(50))
BEGIN
SELECT DATE_FORMAT(mydate,'%y年%m月%d日') INTO strDate;
END $
# 调用
CALL test_pro4(NOW(),@m)$
# 查看结果
SELECT @m $
# 5、创建存储过程实现传入女神名,返回:女神 AND 男神 格式的字符串
CREATE PROCEDURE test_pro5(IN beautyName VARCHAR(20),
OUT str VARCHAR(50))
BEGIN
SELECT CONCAT(beautyName,' AND ',IFNULL(boyName,'null')) INTO str
FROM boys bo RIGHT JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
WHERE b.`name`=beautyName;
END $
# 调用
CALL test_pro5('小昭',@m)$
# 查看结果
SELECT @m$
# 6、创建存储过程,根据传入的起始索引和条目数,查询beauty表的记录
CREATE PROCEDURE test_pro6(IN startIndex INT,IN size INT)
BEGIN
SELECT * FROM beauty LIMIT startIndex,size;
END $
# 调用
CALL test_pro6(3,5)$ #第3行之后的5条记录
#---------------------------------------------------------------------------------------
十六、函数
# 函数(相当于自定义函数)
/*好处:
1、提高代码的重用性
2、简化操作
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高效率
含义:一组预先编译好的SQL语句的集合、理解成批处理语句
与存储过程的区别:
存储过程:可以有0个返回,也可以有多个返回,适合批量的插入或更新(进行增删改)
函数:有且仅有1个返回,适合处理数据后返回一个具体结果(进行具体计算返回值)
(函数是可以嵌入在sql中使用的,可以在select中调用,而存储过程不行)
*/
/*一、创建语法
create function 函数名(参数列表) returns 返回类型
begin
函数体
end
注意:
1、参数列表包含两部分:参数名 参数类型
2、函数体:肯定会有return语句 return 值;
(如果return语句没有放在函数体的最后也不报错,但不建议这么做)
3、函数体中仅有一句话,则可以省略begin end
4、使用delimiter语句设置结束标记
*/
# 二、调用语法
# select 函数名(参数列表)
# 1、无参有返回
# 返回公司的员工个数
DELIMITER $
CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0; #局部变量声明
SELECT COUNT(*) INTO c#变量赋值
FROM employees;
RETURN c;#返回结果
END $
# 调用
SELECT myf1()$
# 2、有参有返回
# 根据员工名,返回他的工资
CREATE FUNCTION myf2(empName VARCHAR(25)) RETURNS DOUBLE
BEGIN
SET @sal=0; #用户变量声明
SELECT salary INTO @sal #赋值
FROM employees
WHERE `last_name`=empName;
RETURN @sal;#返回结果
END $
# 调用
SELECT myf2('K_ing')$ #由于叫K_ing的有两人,故无法返回(函数只能返回一个结果)
SELECT myf2('Kochhar')$ # 可执行
# 根据部门名,返回该部门的平均工资
CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
DECLARE sal DOUBLE; #用 set @c;也行
SELECT AVG(salary) INTO sal
FROM departments d JOIN employees e
ON d.`department_id`=e.`department_id`
WHERE d.`department_name`=deptName;
RETURN sal;
END $
# 调用
SELECT myf3('IT')$
#三、查看函数
SHOW CREATE FUNCTION myf3;
#四、删除函数
DROP FUNCTION myf3;
#-----------------------------------------------练习练习练习-----------------------------
# 创建函数,实现传入两个float,返回二者的和
CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
DECLARE SUM FLOAT DEFAULT 0;#局部变量声明
SELECT SUM=num1+num2; #赋值
RETURN SUM;#返回
END $
#调用
SELECT test_fun1(1,2)$
#---------------------------------------------------------------------------------------
十七、流程控制
# 流程控制结构
/*
顺序结构:程序从上往下依次执行
分支结构:程序从多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码
*/
#一、分支结构
/*1、if函数
功能:实现简单是双分支
语法:select if(表达式1,表达式2,表达式3)
执行顺序:若表达式1成立,则返回表达式2的值,否则返回表达式3的值
应用:任何地方 */
/*2、case结构
功能:实现多个等值判断或区间判断
语法:
方式一:
case 要判断的变量、字段或表达式
when 要判断的值 then 要返回的值1或语句1;
when 要判断的值 then 要返回的值2或语句2;
...
else 要返回的值n或语句n;
end
方式二:
case
when 条件1 then 要返回的值1或语句1
when 条件2 then 要返回的值2或语句2
...
else 要返回的值n或语句n
end
特点:①
可以作为表达式嵌套在其他语句中使用,可以放在任何地方(begin end中或begin end外)
可以作为独立的语句使用,只能放在begin end中,要显示结果需加select
②
如果when中的值或条件成立,则执行对应的then后面的语句,并且结束case
如果都不满足,则执行else中的语句或值
③
else可以省略,如果else省略了,且所有when条件都不满足,则返回null
*/
/* 创建存储过程,根据传入的成绩,来显示等级
比如传入的成绩:90-100显示A
80-90显示B
60-80显示C
否则显示D */
DELIMITER $
CREATE PROCEDURE test_case(IN score INT)
BEGIN
CASE # case前无select
WHEN score>=90 AND score<=100 THEN SELECT 'A';#注意这里要加select
WHEN score>=80 THEN SELECT 'B';
WHEN score>=60 THEN SELECT 'C';
ELSE SELECT 'D'
END CASE;
END $
# 调用
CALL test_case(95)$
/*3、if结构
功能:实现多重分支
语法:if 条件1 then 语句1;
elseif 条件2 then 语句2;
...
else 语句n;
end if;
应用:只能用在begin end中 */
/*根据传入的成绩,来返回等级
比如传入的成绩:90-100返回A
80-90返回B
60-80返回C
否则返回D */
CREATE FUNCTION test_if(score INT) RETURNS CHAR #char类型
BEGIN
IF score>=90 AND score<=100 THEN RETURN 'A';#注意这里加return
ELSEIF score>=80 THEN RETURN 'B';
ELSEIF score>=60 THEN RETURN 'C';
ELSE RETURN 'D';
END IF;
END $
# 调用
SELECT test_if(86)$
#二、循环结构
/*
分类:while、loop、repeat
循环控制:
iterate:类型与continue,结束本次循环,继续下一次
leave:类似于break,跳出,结束当前所在的循环
*/
#1、while(先判断后执行)
/*
语法:(如果要加循环控制,就要起标签)
【标签:】while 循环条件 do
循环体;
end while 【标签】; */
#2、loop
/*
语法:
【标签:】loop
循环体;
end loop 【标签】;
(可以用来模拟简单的死循环) */
#3、repeat(先执行后判断)
/*
语法:
【标签:】repeat
循环体;
until 结束循环的条件
end repeat 【标签】; */
# 批量插入,根据次数插入到admin表中多条记录(没有添加循环控制)
CREATE PROCEDURE pro_while1(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_while1(100)$
# 批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止(添加leave循环控制)
CREATE PROCEDURE pro_while2(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;
a:WHILE i<=insertCount DO
INSERT INTO admin(`username`,`password`) VALUES(CONCAT('xiaohua',i),'0000');
IF i>=20 THEN LEAVE a; # leave相当于break结束循环,注意加标签
END IF;
SET i=i+1;
END WHILE a;
END $
# 调用
CALL pro_whlie2(100)$
# 批量插入,根据次数插入到admin表中多条记录,只插入偶数次(添加iterate循环控制)
CREATE PROCEDURE pro_while3(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;# iterate跳过这次循环继续下次循环
END IF;
INSERT INTO admin(`username`,`password`) VALUES(CONCAT('xiaohua',i),'0000');
END WHILE a;
END $
# 调用
CALL pro_while3(100)$
#---------------------------------------练习练习练习-------------------------------------
/*一、已知表stringcontent
其中字段:
id 自增长
content varchar(20)
向该表插入指定个数的随机的字符串 */
CREATE TABLE IF NOT EXISTS stringcontent (id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(20));
DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;#定义一个循环变量1,表示插入次数
DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
DECLARE startIndex INT DEFAULT 1;#代表起始索引
DECLARE len INT DEFAULT 1;#代表截取的字符长度
WHILE i<=insertCount DO
SET len=FLOOR(RAND()*(20-startIndex+1)+1);#产生一个随机整数,代表截取长度,1-(26-startIndex+1)
SET startIndex=FLOOR(RAND()*26+1);#产生一个随机整数,代表起始索引1-26(rand()随机函数包含0不包含1)
INSERT INTO stringcontent(content) VALUES (SUBSTR(str,startIndex,len));
SET i=i+1;#循环变量更新
END WHILE;
END $
# 调用
CALL test_randstr_insert(10)$
# 查看结果
SELECT * FROM `stringcontent`$
#---------------------------------------------------------------------------------------