MySQL入门代码(附案例)

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`$
#---------------------------------------------------------------------------------------

 

你可能感兴趣的:(MySQL入门代码(附案例))