Java知识

二、数据库

2.1DQL(Data Query Language):数据查询语言,用来查询记录(数据)

-- DQL(Data Query Language)数据查询语言  select
########基础查询########
/*
	一.语法
		 select 查询列表  from  表;   
		 PS:查询列表:字段、表达式、常量、函数等
			
	二.执行顺序
		 from -> select
*/
select * from girl
insert into girl VALUES(null,' 喻佳莉',DEFAULT,?,?,null,?); // ?生日吗 ?电话 ? 男盆友
insert into girl VALUES(null,' 喻佳莉',DEFAULT,'2001-1-5',13972049598,null,null);
INSERT into china VALUES(5,'123',12)

truncate table china

########三.案例########
-- 一、查询常量
select 12;

-- 二、查询表达式
select version();  -- 查询版本8.0.28
select database(); -- 查询当前使用的数据库testdql

-- 三、查询单个字段
select first_name from employees;

-- 四、查询多个字段
select first_name , last_name from employees;

-- 五、查询所有字段
select * from employees;
    
-- 六、查询函数(调用函数,获取返回值)
-- concat(字段,字符串)
select concat(first_name , ' $ ' , last_name) name from employees;

-- 七、给查询字段起别名
#方式一:使用as关键字
select concat(first_name , ' $ ' , last_name) as name from employees;
#方式二:使用空格
select concat(first_name , ' $ ' , last_name) name from employees;

-- 八、+的作用
-- 1.两个操作数都是数值型,执行加法操作
select 1+2; -- 3
-- 2.其中一个操作数为字符型,一个操作数为数值,则将字符型数据强制转换成数值型,如果无法转换,则直接当做0处理
select '12a'+3; -- 12+3=15
select 'a12'+3; -- 0+3=3
-- 3.其中一个操作数为null,结果为null  (null是无穷大)
select null+null; -- null
select null+3;    -- null
select first_name , commission_pct , commission_pct*salary from employees;

-- 九、distinct的使用,去除重复记录
#需求:查询员工涉及到的部门编号
select distinct department_id from employees;

-- 十、查看表的结构
desc employees;

########四.作业########
-- 1. 下面的语句是否可以执行成功
select last_name , job_id , salary as sal from employees;
-- 2. 下面的语句是否可以执行成功 
select * from employees;
-- 3. 找出下面语句中的错误  错误是使用中文的符号,一定要使用英文的符号
-- select employee_id , last_name, salary * 12 “ANNUAL SALARY” from employees;
select employee_id , last_name , salary * 12 "ANNUAL SALARY" from employees;
-- 4. 显示表 departments 的结构,并查询其中的全部数据
desc departments;
select * from departments;
-- 5. 显示出表 employees 中的全部 job_id(不能重复)
select DISTINCT job_id from employees;
-- 6. 显示出表 employees 的全部列,各个列之间用逗号连接,列头显示成 OUT_PUT
select CONCAT(employee_id ,' , ',first_name, ' , ',salary) OUT_PUT from employees;

########条件查询########
/*
	一.语法
		 select 查询列表  from  表  where 筛选条件;   
			
	二.执行顺序
		 from -> where -> select
*/
########三.案例########
-- 一、按关系表达式筛选  关系运算符:>  <  >=  <=  =  <>    补充:也可以使用!=,但不建议
#案例1:查询部门编号不是100的员工信息
select first_name , department_id
from employees
where department_id<>100;

#案例2:查询工资<15000的姓名、工资
select first_name , salary
from employees
where salary<15000;

-- 二、按逻辑表达式筛选 逻辑运算符:and  or  not   补充:也可以使用&&  ||  !  ,但不建议
#案例1:查询部门编号不是 50-100之间员工姓名、部门编号、邮箱
select first_name , department_id , email
from employees
where department_id <50 or department_id>100;

select first_name , department_id , email
from employees
where department_id not between 50 and 100;

#案例2:查询奖金率>0.03 或者 员工编号在60-110之间的员工信息
select employee_id , first_name , commission_pct
from employees
where (commission_pct>0.3) or (employee_id between 60 and 110);

-- 三、模糊查询 like/not like 
-- %任意模糊匹配  _模糊匹配一个位置  \转义  escape声明符号充当转义字符
#案例1:查询姓名中包含字符a的员工信息
select first_name
from employees
where first_name like '%a%';

select first_name
from employees
where first_name like concat('%','a','%');

#案例2:查询姓名中包含最后一个字符为e的员工信息
select first_name
from employees
where first_name like '%e';

#案例3:查询姓名中包含第一个字符为e的员工信息
select first_name
from employees
where first_name like 'e%';

#案例4:查询姓名中包含第三个字符为x的员工信息
select first_name
from employees
where first_name like '__x%';

#案例5:查询姓名中包含第二个字符为_的员工信息
select last_name
from employees
where last_name like '_\_%';

-- 默认\转义字符,escape声明符号充当转义字符
select last_name
from employees
where last_name like '_$_%' escape '$';


-- 四、查询某字段的值是否属于指定的列表之内  in/not in
#案例1:查询部门编号是30/50/90的员工名、部门编号
select first_name , department_id
from employees
where department_id in(30,50,90);

#案例2:查询工种编号不是SH_CLERK或IT_PROG的员工信息
select first_name , job_id
from employees
where job_id not in('SH_CLERK','IT_PROG');

-- 五、判断某个字段的值是否介于指定的区间范围  between and/not between and
#案例1:查询部门编号是30-90之间的部门编号、员工姓名
select first_name , department_id
from employees
where department_id between 30 and 90;

#案例2:查询年薪不是100000-200000之间的员工姓名、工资、年薪
select first_name , salary , salary*12 年薪
from employees
where salary*12 not between 100000 and 200000;

-- IFNULL(表达式1,表达式2) 表达式1若为null则显示表达式2的值,若不为null则直接显示
select first_name , salary , salary*12 年薪 , 12*salary*(1+IFNULL(commission_pct,0)) 总收入
from employees
where 12*salary*(1+IFNULL(commission_pct,0)) not between 100000 and 200000;

-- 六、查询是null字段  is null , 查询不是null字段  is not null
#案例1:查询没有奖金的员工信息
select first_name , salary , commission_pct
from employees
where commission_pct is null;

#案例2:查询有奖金的员工信息
select first_name , salary , commission_pct
from employees
where commission_pct is not null;

########四.作业########
-- 1. 查询工资大于 12000 的员工姓名和工资
select first_name , salary from employees where salary>12000;

-- 2. 查询员工号为 176 的员工的姓名和部门号和年薪
select first_name , department_id , salary*12 from employees where employee_id=176;

-- 3. 选择工资不在 5000 到 12000 的员工的姓名和工资
select first_name , salary from employees where salary not BETWEEN 5000 and 12000;

-- 4. 选择在 20 或 50 号部门工作的员工姓名和部门号
select first_name , department_id from employees where department_id in(20,50);

-- 5. 选择公司中没有管理者的员工姓名及 job_id
select first_name , job_id from employees where manager_id is null;

-- 6. 选择公司中有奖金的员工姓名,工资和奖金级别
select first_name , salary , commission_pct from employees where commission_pct is not null;

-- 7. 选择员工姓名的第三个字母是 a 的员工姓名
select first_name from employees where first_name like '__a%';

-- 8. 选择姓名中有字母 a 和 e 的员工姓名
select first_name from employees where first_name like '%a%e%' or first_name like '%e%a%';

-- 9. 显示出表 employees 表中 first_name 以 'e'结尾的员工信息
select first_name from employees where first_name like '%e';

-- 10. 显示出表 employees 部门编号在 80-100 之间 的姓名、职位
select first_name , job_id from employees where department_id BETWEEN 80 and 100;

-- 11. 显示出表 employees 的 manager_id 是 100,101,110 的员工姓名、职位
select first_name , job_id from employees where manager_id in(100,101,110);

########排序查询########
/*
	一.语法
		 select 查询列表  from  表  where 筛选条件 order by 排序列表;   
		 PS:
		 1、排序列表可以是单个字段、多个字段、表达式、函数、列数、以及以上的组合
		 2、升序,通过asc,默认行为
			  降序,通过desc
			
	二.执行顺序
		 from -> where -> select -> order by
*/
########三.案例########
-- 一、按单个字段排序
#案例1:将员工编号>120的员工信息进行工资的升序
select employee_id , first_name , salary
from employees
where employee_id>120
order by salary;

#案例2:将员工编号>120的员工信息进行工资的降序
select employee_id , first_name , salary
from employees
where employee_id>120
order by salary desc;

-- 二、按表达式排序
#案例:对有奖金的员工,按年薪降序
select first_name , salary*12 年薪 , commission_pct
from employees
where commission_pct is not null
order by salary*12 desc;

-- 三、按别名排序
#案例:对有奖金的员工,按年薪降序
select first_name , salary*12 年薪 , commission_pct
from employees
where commission_pct is not null
order by 年薪 desc;

-- 四、按函数的结果排序
#案例:按姓名的字数长度进行升序
-- length()字节长度  char_length()字符个数
select length('abc') , char_length('abc');   
select length('张三') , char_length('张三');
select first_name , CHAR_LENGTH(first_name) '字符个数' from employees order by CHAR_LENGTH(first_name);

-- 五、按多个字段排序
#案例:查询员工的姓名、工资、部门编号,先按工资升序,再按部门编号降序
select first_name , salary , department_id
from employees
order by salary , department_id desc;

-- 六、按列数排序(不做要求)
#案例:按第二列排序
select * from employees order by 2;
select * from employees order by first_name;

########四.作业########
-- 1. 查询员工的姓名和部门号和年薪,按年薪降序 按姓名升序
select first_name , department_id , salary*12
from employees
order by salary*12 desc , first_name;

-- 2. 选择工资不在 8000 到 17000 的员工的姓名和工资,按工资降序
select first_name , salary
from employees
where salary not between 8000 and 17000
order by salary desc;

-- 3. 查询邮箱中包含 e 的员工信息,并先按邮箱的字节数降序,再按部门号升序
select first_name , email , length(email) , department_id
from employees
where email like '%e%'
order by length(email) desc , department_id asc;


########常见函数########
/*
	一.语法
		 select 查询列表  from  表  where 筛选条件 order by 排序列表;   
		 PS:
		 1、排序列表可以是单个字段、多个字段、表达式、函数、列数、以及以上的组合
		 2、升序,通过asc,默认行为
			  降序,通过desc
			
	二.执行顺序
		 from -> where -> select -> order by
*/
######## 三.案例 ########
 
########字符串函数########
-- 1、CONCAT 拼接字符
-- 用法:CONCAT(str1,str2,...) 将多个字符串拼接在一起
select CONCAT(first_name,'_',last_name) name from employees;

-- 2、LENGTH 获取字节长度
-- 用法:LENGTH(str) 返回字符串的字节数,注意中文UTF-8,一个汉字占3个字节
select LENGTH('中');  -- 3
select LENGTH('abc'); -- 3

-- 3、CHAR_LENGTH 获取字符个数
-- 用法:CHAR_LENGTH(str) 返回字符串的字符数
select CHAR_LENGTH('中');  -- 1
select CHAR_LENGTH('abc'); -- 3

-- 4、INSERT 插入新字符串
-- 用法:INSERT(str,pos,len,newstr) 
-- 第一个参数str:操作的字符串  第二个参数pos:起始位置,默认从1开始  第三个参数len:长度  第四个参数:新字符串
select INSERT('abcde',1,2,'AAA'); -- AAAcde
select INSERT('abcde',1,0,'BBB'); -- BBBabcde

-- 5、SUBSTRING 截取子串  注意:起始索引从1开始
-- 用法:SUBSTRING(str,pos,len)
-- 第一个参数str:操作的字符串 第二个参数pos:起始位置,默认从1开始 第三个参数len:截取的长度
select first_name , SUBSTRING(first_name,2,3) from employees;
select first_name , SUBSTRING(first_name,2) from employees;

-- 6、UPPER/LOWER  变大写/变小写
#案例:查询员工表的姓名,要求格式:姓首字符大写,其他字符小写,名所有字符大写,且姓和名之间用_分割,最后起别名“OUTPUT”
select CONCAT(UPPER(SUBSTRING(last_name,1,1)), LOWER(SUBSTRING(last_name,2)) ,'_',UPPER(first_name))
from employees;

-- 7、LEFT/RIGHT  截取子串
-- 用法:LEFT(str,len)  从左边截取,第一个参数str:操作的字符串 第二个参数len:截取的长度
-- 用法:RIGHT(str,len) 从右边截取,第一个参数str:操作的字符串 第二个参数len:截取的长度
select 'abcde' , LEFT('abcde',3) , RIGHT('abcde',3);

-- 8、LPAD/RPAD  左填充/右填充
-- 用法:LPAD(str,len,padstr) 从左边填充数据,第一个参数str:操作的字符串 第二个参数len:填充的长度  第三个参数:填充的数据
-- 用法:RPAD(str,len,padstr) 从右边填充数据,第一个参数str:操作的字符串 第二个参数len:填充的长度  第三个参数:填充的数据
select 'abcde' , LPAD('abcde',10,'A') , RPAD('abcde',10,'B');

-- 9、TRIM去前后指定的字符,默认是去空格
-- 用法:TRIM([remstr FROM] str) 默认去除左右空格
select '   abc   ' , LENGTH('   abc   '), TRIM('   abc   ') , LENGTH(TRIM('   abc   '));
-- 用法:LTRIM(str) 默认去除左边空格
select '   abc   ' , LENGTH('   abc   '), LTRIM('   abc   ') , LENGTH(LTRIM('   abc   '));
-- 用法:RTRIM(str) 默认去除右边空格
select '   abc   ' , LENGTH('   abc   '), RTRIM('   abc   ') , LENGTH(RTRIM('   abc   '));
-- 用法:TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str)
-- 第一个参数:BOTH两边移除,LEADING左边移除,TRAILING右边移除  第二个参数:remstr移除的字符串 第三个参数:FROM关键字  第四个参数:操作的字符串
select '###abc###' , TRIM(BOTH '#' from '###abc###') ;  
select '###abc###' , TRIM(LEADING '#' from '###abc###') ;
select '###abc###' , TRIM(TRAILING '#' from '###abc###') ;

-- 10、REPEAT(str,count)  第一个参数:操作的字符串   第二个参数:重复的次数
select REPEAT('abc',3);

-- 11、REPLACE(str,from_str,to_str) 第一个参数:操作的字符串  第二个参数:将要替换的字符串  第三个参数:替换成字符串
select replace('abcdef' , 'bc' , 'AAAAA');

-- 12、STRCMP 比较两个字符大小
select STRCMP('aaa','aba');
select STRCMP('bcd','aba');

-- 13、INSTR获取字符第一次出现的索引
-- 用法:INSTR(str,substr)  第一个参数:操作的字符串  第二个参数:查找的字符串
select INSTR('abcade','a'); -- 1
select INSTR('Abcade','a'); -- 1
select INSTR('bbcade','a'); -- 4

-- 14、SUBSTR(str FROM pos) 
select first_name , SUBSTR(first_name from 2) from employees;
select first_name , SUBSTR(first_name , 2) from employees;


########数学函数########
-- 1、ABS 绝对值
select ABS(-10);    -- 10

-- 2、CEIL 向上取整  返回>=该参数的最小整数
select CEIL(-10.4); -- -10
select CEIL(10.4);  -- 11

-- 3、FLOOR 向下取整,返回<=该参数的最大整数
select FLOOR(-10.6); -- -11
select FLOOR(10.6);  -- 10

-- 4、ROUND 四舍五入
select ROUND(10.6); -- 11
select ROUND(10.2); -- 10
select ROUND(10.666,1); -- 10.7

-- 5、TRUNCATE 截断
select TRUNCATE(10.666,1); -- 10.6

-- 6、MOD 取余
select MOD(10,3); -- 1

-- 7、RAND() 返回0~1的随机值
select RAND();


########日期时间函数########
-- 1、NOW()  系统当前时间
select NOW();     -- 2022-11-30 15:41:07
select SYSDATE(); -- 2022-11-30 15:41:13

select YEAR(NOW());   -- 2022
select MONTH(NOW());  -- 11
select DAY(NOW());    -- 30
select HOUR(NOW());   -- 15
select MINUTE(NOW()); -- 44
select SECOND(NOW()); -- 14

-- 获取一年的第几周
select WEEK(NOW());   -- 48
select WEEKOFYEAR(now()); -- 48

select DAYOFWEEK(now()); -- 返回当前是周几,注意:周日是1,周一是2,。。。周六是7
select WEEKDAY(now());   -- 返回当前是周几,注意,周1是0,周2是1,。。。周日是6
select DAYNAME(now());   -- Wednesday
select MONTHNAME(now()); -- November

-- 2、CURDATE  系统当前日期
select CURDATE();
select CURRENT_DATE();

-- 3、CURTIME  系统当前时间
select CURTIME();
select CURRENT_TIME();

select CURRENT_TIMESTAMP();

-- 4、DATEDIFF(date1,date2)  返回date1 - date2的日期间隔
-- TIMEDIFF(time1, time2)返回time1 - time2的时间间隔
select DATEDIFF('2022-10-30','2022-11-30');
select TIMEDIFF('15:15:15','14:15:15');

-- 5、DATE_FORMAT(datetime ,format)  将时间类型,按照指定的字符串格式转换成字符串类型
#案例:查看100号员工入职日期
select hiredate , DATE_FORMAT(hiredate,'%d%m-%y') from employees where employee_id=100;
select hiredate , DATE_FORMAT(hiredate,'%Y-%m-%d %H:%i:%s %W') from employees;

-- 6、STR_TO_DATE(str,format) 将字符串类型,按照指定的字符串格式转换成时间类型
#案例:查看1998年6月以前入职的员工信息
select first_name , hiredate
from employees
where hiredate < STR_TO_DATE('1998年6月','%Y年%m月') ;


-- 7、DATE_ADD(datetime, INTERVALE  expr  type)  返回与给定日期时间相差INTERVAL时间段的日期时间
/*
	使用 DATE_ADD(NOW(),INTERVAL 1 MONTH) 这个函数来进行修改时间
	第一个参数是要修改的时间;
	第二个参数固定写法;
	第三个参数的修改的值 : 如果正数就是加,负数就是减;
	第四个参数可填YEAR,MONTH,DAY,HOUR,MINUTE,SECOND;
*/
select NOW();  -- 2022-11-30 16:01:10
select DATE_ADD(now(),INTERVAL 1 YEAR);   -- 2023-11-30 16:01:10
select DATE_ADD(now(),INTERVAL -1 MONTH); -- 2023-10-30 16:01:10
select DATE_ADD(now(),INTERVAL '-1_-1' YEAR_MONTH);  -- 2021-10-30 16:00:58
select DATE_SUB(now(),INTERVAL 1 HOUR);   -- 2022-11-30 15:01:10
select DATE_SUB(now(),INTERVAL '1_1' HOUR_MINUTE);   -- 2022-11-30 15:00:10


########流程控制函数########
-- 1、IF函数  用法类似于三目运算符
-- 用法:IF(expr1,expr2,expr3) 第一个表达式成立则显示第二个表达式的值,否则显示第三个表达式的值  
#需求:如果有奖金,则显示最终奖金,如果没有,则显示0
select commission_pct , IFNULL(commission_pct,0) from employees;
select commission_pct , IF(commission_pct is null,0,commission_pct) from employees;

select first_name , IFNULL(manager_id,'Boss') from employees;
select first_name , IF(manager_id is null,'Boss',manager_id) from employees;

-- 2、CASE函数
/*
	情况1 :类似于switch语句,可以实现等值判断
	CASE 表达式
	WHEN 值1 THEN 结果1
	WHEN 值2 THEN 结果2
	...
	ELSE 结果n
	END
*/
#案例:部门编号是30,工资显示为2倍;部门编号是50,工资显示为3倍;部门编号是60,工资显示为4倍;否则不变;
#显示部门编号,新工资,旧工资
select department_id 部门编号, salary 旧工资 ,
(
	case department_id
	when 30 then salary*2
	when 50 then salary*3
	when 60 then salary*4
	else salary
	end
) 新工资
from employees;


/*
	情况2:类似于多重IF语句,实现区间判断
	CASE 
	WHEN 条件1 THEN 结果1
	WHEN 条件2 THEN 结果2
	...
	ELSE 结果n
	END
*/
#案例:如果工资>20000,显示级别A;工资>15000,显示级别B;工资>10000,显示级别C;否则,显示D
select first_name , salary , 
(
	case
	when salary>20000 then '级别A'
	when salary>15000 then '级别B'
	when salary>10000 then '级别C'
	else '级别D'
	end
) level
from employees;


########分组(聚合)函数########
-- 案例:查询员工信息表中,所有员工的工资和、工资平均值、最低工资、最高工资、有工资的个数
select sum(salary) , avg(salary) , min(salary) , max(salary) , count(salary) from employees;

-- count() 函数
#添加筛选条件
-- 案例1:查询employees表中记录数
select count(*) from employees;

-- 案例2:查询employees表中有佣金的人数
select count(commission_pct) from employees;
select count(*) from employees where commission_pct is not null;

-- 案例3:查询employees表中月薪大于2500的人数
select count(*) from employees where salary>2500;

-- 案例4:查询有领导的人数
select count(*) from employees where manager_id is not null;

#其它用途
-- 案例5:统计结果集的行数,推荐使用count(*)。需求:查询员工表中30号部门的人数
select count(*) from employees where department_id=30;

-- 案例6:搭配distinct实现去重的统计。需求:查询有员工的部门个数
select count(DISTINCT department_id) from employees;

-- SUM() 求和函数
-- 案例7:查询所有员工月薪和
select SUM(salary) from employees;

-- AVG() 平均值函数
-- 案例8:统计所有员工平均工资
select AVG(salary) from employees;

-- MAX() 和 MIN()  最大值和最小值函数
-- 案例9:查询最高工资和最低工资
select MAX(salary) from employees;
select MIN(salary) from employees;

########四.作业########
########常见函数作业########
-- 1. 显示系统时间(注:日期+时间)
select NOW();
-- 2. 查询员工号,姓名,工资,以及工资提高百分之 20%后的结果(new salary)
select employee_id,first_name,salary,salary * 1.2 'new salary'
from employees;
-- 3. 将员工的姓名按首字母排序,并写出姓名的长度(length)
select first_name,length(first_name)
from employees
order by first_name;
-- 4. 做一个查询,产生下面的结果
--  earns  monthly but wants 
-- Dream Salary
-- King earns 24000 monthly but wants 72000
select concat(last_name,' earns ',salary,' monthly but wants ',salary * 3) 'Dream Salary'
from employees;
-- 5. 使用 case-when,按照下面的条件:
-- job grade
-- AD_PRES A
-- ST_MAN B
-- IT_PROG C
-- SA_REP D
-- ST_CLERK E
-- 产生下面的结果
-- Last_name Job_id Grade
-- king AD_PRES A
select last_name,job_id,
case job_id
when 'AD_PRES' then 'A'
when 'ST_MAN' then 'B'
when 'IT_PROG' then 'C'
when 'SA_REP' then 'D'
when 'ST_CLERK' then 'E'
end grade
from employees;

########分组函数作业########
-- 1. 查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees;
-- 2. 查询员工表中的最大入职时间和最小入职时间的相差天数 (DIFFRENCE)
SELECT DATEDIFF(Max(hiredate),MIN(hiredate))
FROM employees;
-- 3. 查询部门编号为 90 的员工个数
SELECT COUNT(*) 
FROM employees
WHERE department_id=90;

########select语句-01题目版########
-- 【1.整表查询】
-- 例子1:desc  查看表结构;
DESC employees;
-- 【2.单表查询】
-- 例子2:查询employees前三个列的内容?
SELECT employee_id,first_name,last_name from employees;
-- 例子3:显示部门中的所有内容?
select * from departments;

-- 例子4:显示每位员工的全名?
select concat(first_name,'-',last_name) from employees;
-- 例子5:员工全名 is in department 部门编号?
select concat(first_name , '-' , last_name , ' is in department ' , department_id) from employees;
-- 例子6:列出每个员工的年薪?
select salary*12 from employees;
-- 例子7:列出每个员工的一年的总收入?
select IF(commission_pct is null , salary*12, salary*12*(1+commission_pct)) 年薪 from employees; 

-- 例子8:列出所有部门的种类?(distinct去除重复的)
select DISTINCT department_name from departments;
-- 例子9:列出各部门有什么不同的职位?(distinct去除重复的)
SELECT DISTINCT job_id , department_id FROM employees ORDER BY department_id;

-- 【3.限定查询】
-- 例子10: 列出50部门的员工的employee_id,名字,salary,和部门号?(条件查询where)
SELECT employee_id , CONCAT(first_name,last_name) AS `name` , salary , department_id FROM employees WHERE department_id = 50;
-- 例子11: 找出工资高于12000元的员工的年薪?
SELECT employee_id ,salary * 12 *(1 + IFNULL(commission_pct,0)) AS `income` , salary FROM employees WHERE salary > 12000;
-- 例子12: 找出年薪高于120000元的员工? 
SELECT employee_id ,salary * 12 *(1 + IFNULL(commission_pct,0)) AS `income` , salary
FROM employees WHERE salary * 12 *(1 + IFNULL(commission_pct,0)) > 120000;
-- 例子13: 找出90部门年薪高于120000元的员工?
SELECT employee_id ,salary * 12 *(1 + IFNULL(commission_pct,0)) AS `income` , salary , department_id
FROM employees WHERE salary * 12 *(1 + IFNULL(commission_pct,0)) > 120000 AND department_id = 90;
-- 例子14: 找出‘Steven’每个月的工资?
SELECT employee_id , first_name , salary FROM employees WHERE first_name = 'Steven';
-- 例子15: 把所有职位为‘ST_CLERK’的员工列出?
SELECT CONCAT(first_name,last_name) AS `name` , job_id FROM employees WHERE job_id = 'ST_CLERK';
-- 例子16: 找出工资在10000-20000之间的员工?
SELECT CONCAT(first_name,last_name) AS `name` , salary FROM employees WHERE salary BETWEEN 10000 AND 20000;
-- 例子17: 找出30, 60, 90 部门的员工的工资?in(30,60,90) 或 or
SELECT CONCAT(first_name,last_name) AS `name` , salary , department_id FROM employees 
WHERE department_id IN(30,60,90) ORDER BY department_id; 

SELECT CONCAT(first_name,last_name) AS `name` , salary , department_id FROM employees 
WHERE department_id=30 or department_id=60 or department_id=90 ORDER BY department_id; 

-- 例子18: 列出哪些员工没有提成?
SELECT CONCAT(first_name,last_name) AS `name`,commission_pct FROM employees WHERE commission_pct is null;
-- 例子19: 列出不在30, 60, 90 部门的员工的工资?
SELECT CONCAT(first_name,last_name) AS `name` , salary , department_id FROM employees 
WHERE department_id NOT IN(30,60,90) ORDER BY department_id;
-- 例子20: 列出 60, 90 部门工资大于10000元的员工信息?
SELECT CONCAT(first_name,last_name) AS `name` , salary , department_id FROM employees 
WHERE department_id IN(60,90) AND salary > 10000 ORDER BY department_id;

-- 【4. 模糊查询】
-- _表示匹配任意一个字符,汉字占一个字符,%是指任意多个字符 ; escape '\' 表示将_或者%,转义成普通字符
-- 例子21:找出first_name第二个字母是 e 的员工信息?
select first_name from employees where first_name like '_e%';
-- 例子22:列出当前DataBase下所有含有字母e的表?
-- show tables from 库名 like 条件;
SHOW TABLES FROM testdql like '%e%';
-- 例子23:找出入职时间是 92年的所有员工信息?
select first_name,hiredate from employees where DATE_FORMAT(hiredate,'%y年') = '92年';

-- 【5. 排序查询  降序是desc,升序是asc,默认升序】
-- 例子24: 按工资降序显示员工的信息?
SELECT salary from employees ORDER BY salary DESC;
-- 例子25: 按提成升序显示员工的信息?
SELECT first_name,commission_pct from employees ORDER BY commission_pct;
-- 例子26: 先工资降序,再按提成升序显示员工?
select first_name,employee_id,salary,commission_pct
from employees
ORDER BY salary desc, commission_pct;
-- 例子27: 按年薪降序显示员工?
select salary,salary * 12
from employees
ORDER BY salary	* 12 DESC;

-- 6.常见函数
-- 【6.1字符函数:操作字符串的函数】
-- 例子28:当不知道‘Steven’在数据库是大小写的时,找出‘Steven’的工资?
select first_name, salary
from employees
where LOWER(first_name) = 'steven';
-- 例子29:列出每个员工名字(last_name)的最后两个字符?
select last_name, right(last_name,2)
from employees;

-- 【6.3日期函数】
-- 例子31:查出下一天、下一分钟、下一秒的时间
select date_add(now(), INTERVAL 1 day);
select date_add(now(), INTERVAL 1 MINUTE);
select date_add(now(), INTERVAL 1 second);

-- 例子32:求某天是星期几
SELECT DAYNAME(NOW());

-- 例子33:找出今年的天数
SELECT DATEDIFF('2022-12-31','2021-12-31');

-- 例子34:今天是一年的第几天
SELECT DATEDIFF(NOW(),'2021-12-31');
select DAYOFYEAR(now());

-- 例子35:闰年的处理方法(如何判断闰年?)
SELECT CASE
	WHEN LAST_DAY(DATE_ADD(CONCAT(DAY(NOW()),'-1-1'),INTERVAL 1 MONTH)) = 29 
	THEN '闰年'
	ELSE '平年'
END 年;

select case 
	when DATEDIFF('2022-12-31','2021-12-31')=365
	then '平年'
	else '闰年'
end 年;	


-- 例子36:按照'%Y%m%d %H%i%s'的格式,输出50部门员工的入职时间?
SELECT DATE_FORMAT(hiredate,'%Y%m%d %H%i%s')
FROM employees
WHERE department_id = 50;

-- 例子37:列出4月份入职的员工?
SELECT first_name,hiredate
FROM employees
WHERE DATE_FORMAT(hiredate,'%m月') = '04月';

SELECT first_name,hiredate
FROM employees
WHERE MONTH(hiredate)= 4;

-- 例子38:求出下个月的1号?
select DATE_ADD(LAST_DAY(now()),INTERVAL 1 DAY);  -- 2023-01-01

-- 例子39:找出92年上半年入职的员工信息? 92年01月~92年06月
SELECT first_name,hiredate
FROM employees
WHERE DATE_FORMAT(hiredate,'%y年%m月') between '92年01月' and '92年06月';

-- 例子40:找出15号后入职的员工信息?
SELECT first_name,hiredate
FROM employees
WHERE DATE_FORMAT(hiredate,'%d')>15;

-- 【6.4组函数:传进去一堆值,只返回一个值,默认对null去除】
-- 例子42:列出提成的平均值?
select avg(commission_pct)
from employees
where commission_pct is not null;

-- 例子43:列出提成的最大值?
select max(commission_pct)
from employees
where commission_pct is not null;

-- 例子44:求出有提成的员工个数?
select count(commission_pct)
from employees;

select count(*)
from employees
where commission_pct is not null;

-- 例子45:求出80部门的平均工资?(只显示平均工资)
select avg(salary)
from employees
where department_id = 80;
 

-- 【8.执行顺序: from --> where --> group by --> having --> select --> order by】
select dept_id,avg(salary)
from employees
where salary>500
group by dept_id
having count(*)>2
order by dept_id;


########分组查询########
/*
	一.语法
		 select 查询列表  from 表名  where 筛选条件 group by 分组列表 having 分组后筛选 order by 排序列表;
			
	二.执行顺序
		 from -> where -> group by -> having -> select -> order by
		 
	三.用法
		 1.where出现在group by前面,针对于原表中的数据进行筛选;
		 2.having出现在group by后面,针对于分组后的数据进行筛选;
		 3.select的查询列表,要么是组函数(count、max、min),要么是分组字段(出现在group by后面的字段);
*/
######## 三.案例 ########
-- 1)简单的分组
#案例1:查询每个工种的员工平均工资
select job_id , avg(salary)
from employees
group by job_id;

#案例2:查询每个领导的手下人数
select manager_id , count(*)
from employees
group by manager_id;

-- 2)可以实现分组前的筛选  where
#案例1:查询邮箱中包含a字符的每个部门的最高工资
select department_id , max(salary)
from employees
where email like '%a%'
group by department_id;

#案例2:查询每个领导手下有奖金的员工的平均工资
select manager_id , avg(salary)
from employees
where commission_pct is not null
group by manager_id;

-- 3)可以实现分组后的筛选  having
#案例1:查询哪个部门的员工个数>5
select department_id , count(*)
from employees
group by department_id
having count(*)>5;

#案例2:每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
select job_id , max(salary)
from employees
group by job_id
having max(salary)>12000;

#案例3:领导编号>102的每个领导手下的最低工资大于5000的最低工资
select manager_id , min(salary)
from employees
where manager_id>102
group by manager_id
having min(salary)>5000;

-- 4)可以实现排序
#案例:查询没有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
select job_id , max(salary)
from employees
where commission_pct is null
group by job_id
having max(salary)>6000
order by max(salary);

-- 5)按多个字段分组
#案例:查询每个工种每个部门的最低工资,并按最低工资降序。提示:工种和部门都一样,才是一组
select job_id , department_id , min(salary)
from employees
group by job_id , department_id
order by min(salary) desc;


######## 四.作业 ########
-- 1. 查询各 job_id 的员工工资的最大值,最小值,平均值,总和,并按 job_id 升序
SELECT job_id,MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees 
GROUP BY job_id
ORDER BY job_id;

-- 2. 查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT MAX(salary)-MIN(salary) DIFFERENCE
FROM employees;

-- 3. 查询各个管理者手下员工的最低工资,其中最低工资不能低于 6000,没有管理者的员工不计算在内
SELECT MIN(salary)
FROM employees
where manager_id is not NULL
GROUP BY manager_id
HAVING MIN(salary)>=6000;

-- 4. 查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
SELECT department_id,count(*),AVG(salary)
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) desc;

-- 5. 选择具有各个 job_id 的员工人数
SELECT job_id,count(*)
FROM employees
GROUP BY job_id;

-- 6. 2014年每个月入职的人数
SELECT MONTH(hiredate) , COUNT(*) FROM employees WHERE YEAR(hiredate) = 2014 GROUP BY MONTH(hiredate);

-- 【7.分组查询  group by表示根据什么条件进行分组,having表示限定分组条件】
-- 例子46:求出50部门的平均工资?(显示部门号、平均工资)
SELECT department_id , AVG(salary) FROM employees WHERE department_id = 50;
SELECT department_id , AVG(salary) FROM employees GROUP BY department_id HAVING department_id = 50  ;

-- 例子47:求出各部门的平均工资?
SELECT department_id , AVG(salary) FROM employees GROUP BY department_id;

-- 例子48:求出各职位的平均工资?(要求:列出职位名称和平均工资)
SELECT job_id , AVG(salary) FROM employees GROUP BY job_id;

-- 例子49:求出各部门不同职位的平均工资?
SELECT department_id , job_id , AVG(salary) FROM employees GROUP BY department_id , job_id;

-- 例子50:求出各部门的平均工资? (要求显示:区域名称、部门编号、部门名称、平均工资)?
SELECT max(city) , max(d.department_id) , department_name , AVG(salary) 
FROM employees e JOIN departments d JOIN locations l 
ON e.department_id = d.department_id AND d.location_id = l.location_id 
GROUP BY department_name;


##########SQL92标准查询##########
-- 一.交叉连接(笛卡尔积查询)  数据冗余
select  count(*)  from  girl g , boy b; -- 12*6=72

-- 二.内连接
/*
	1.语法
	select 查询列表
  from 表1 别名 , 表2 别名
  where 连接条件 and 筛选条件
  group by 分组列表
  having 分组后筛选
  order by 排序列表
	
	2.执行顺序
	from -> where先执行连接条件再执行筛选条件 -> group by -> having -> select -> order by
*/
########## 等值连接 ##########
-- 1、多表等值查询
#案例1:查询女神名和对应的男神名
select g.name '女神名' , b.name '男神名'
from girl g , boy b
where g.boyfriend_id = b.id;

#案例2:查询员工名和对应的部门名
select e.first_name '员工名' , d.department_name '部门名'
from employees e , departments d
where e.department_id = d.department_id;

-- 2、为表起别名
#查询员工名、工种号、工种名
select e.first_name '员工名' , e.job_id '工种号' , j.job_title '工种名'
from employees e , jobs j
where e.job_id = j.job_id;

-- 3、两个表的顺序可以调换
#查询员工名、工种号、工种名
select e.first_name '员工名' , e.job_id '工种号' , j.job_title '工种名'
from jobs j , employees e
where e.job_id = j.job_id;

-- 4、可以加筛选
#案例1:查询有奖金的员工名、部门名
select e.first_name '员工名' , d.department_name '部门名'
from employees e , departments d
where e.department_id = d.department_id and e.commission_pct is not null;

#案例2:查询城市名中第二个字符为o的部门名和城市名
select d.department_name '部门名' , l.city '城市名'
from departments d , locations l
where d.location_id = l.location_id and l.city like '_o%';

-- 5、可以加分组
#案例1:查询每个城市的部门个数
select l.city , count(*) '部门个数'
from departments d , locations l
where d.location_id = l.location_id
group by l.city;

#案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
select d.department_name '部门名' , d.manager_id '领导编号' , min(salary) '最低工资'
from employees e , departments d
where e.department_id = d.department_id
group by d.department_name;

-- 6、可以加排序
#案例:查询每个工种的工种名和员工的个数,并且按员工个数降序
select j.job_title '工种名' , count(*) '员工人数'
from employees e , jobs j
where e.job_id = j.job_id
group by j.job_title
order by count(*) desc;

-- 7、可以实现三表连接?
#案例1:查询员工名、部门名和所在的城市
select e.first_name '员工名', d.department_name '部门名', l.city '城市'
from employees e , departments d , locations l
where e.department_id = d.department_id and d.location_id = l.location_id; 

#案例2:查询员工名、部门名和所在的城市,要求城市名称以s开头,并且按照部门名称降序。
select e.first_name '员工名', d.department_name '部门名', l.city '城市'
from employees e , departments d , locations l
where e.department_id = d.department_id and d.location_id = l.location_id and l.city like 's%'
order by d.department_name desc; 

########## 非等值连接 ##########
#案例:查询员工的工资和工资级别
select e.first_name , e.salary , g.grade_level
from employees e , job_grades g
where e.salary between g.lowest_sal and g.highest_sal;

########## 自连接 ##########
#案例:查询员工名和上级的名称
select e.first_name '员工' , m.first_name '领导'
from employees e , employees m
where e.manager_id = m.employee_id;

-- 三.作业
-- 1. 显示所有员工的姓名,部门号和部门名称。
select e.first_name,e.department_id,d.department_name
from employees e , departments d
where e.department_id=d.department_id;

-- 2. 查询 90 号部门员工的 job_id 和 90 号部门的 location_id
select e.department_id,e.job_id,d.location_id
from employees e , departments d
where e.department_id=d.department_id and d.department_id=90;

-- 3. 选择 所有有奖金的 员工的last_name , department_name , location_id , city
select e.last_name,d.department_name,l.location_id,l.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;

-- 4. 选择city在Toronto工作的员工的last_name , job_id , department_id , department_name
select e.last_name, e.job_id , d.department_name,l.city
from employees e , departments d , locations l
where e.department_id=d.department_id and d.location_id= l.location_id and l.city='Toronto';

-- 5.查询每个工种、每个部门的 部门名、工种名 和最低工资
select d.department_name,j.job_title,MIN(salary) as '最低工资'
from employees e , departments d ,jobs j
where e.department_id=d.department_id and e.job_id= j.job_id
GROUP BY d.department_id,e.job_id;

select d.department_name,j.job_title,MIN(salary) as '最低工资'
from employees e join departments d
on e.department_id=d.department_id
join jobs j
on e.job_id= j.job_id
GROUP BY d.department_id,e.job_id;

-- 6.查询每个国家下的部门个数大于 2 的国家编号
select l.country_id , count(d.department_id) '部门个数'
from departments d , locations l
where  d.location_id = l.location_id
group by l.country_id
having count(d.department_id)>2;

-- 7、选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
-- employees Emp# manager Mgr#
-- kochhar 101 king 100
select e.first_name 'employees' , e.employee_id 'Emp#' , m.first_name 'manager' , m.employee_id 'Mgr#'
from employees e , employees m
where e.manager_id = m.employee_id;


##########SQL99标准查询##########
/*
	1.SQL92标准语法  
			select 查询列表 
			from 表1 , 表2  
			where 连接条件 and 筛选条件  
			group by 分组字段 having 分组条件  
			order by  排序字段; 
	
	2.SQL99标准语法
			select 查询列表 
			from 表1 left | right | full [inner | outer] join 表2
			on 连接条件
			where 筛选条件
			group by 分组字段  having 分组条件
			order by 排序字段;
	
  3.执行顺序
	from -> on先执行连接条件 -> where再执行筛选条件 -> group by -> having -> select -> order by
*/

##########一.内连接##########
/*
		2.SQL99标准语法
			select 查询列表 
			from 表1 [inner] join 表2
			on 连接条件
			where 筛选条件
			group by 分组字段  having 分组条件
			order by 排序字段;
*/
##########等值连接
-- 1.简单连接
#案例:查询员工名和部门名
select e.first_name '员工名' , d.department_name '部门名'
from employees e join departments d
on e.department_id = d.department_id;

-- 2.添加筛选条件
#案例:查询部门编号>100的部门名和所在的城市名
select d.department_name '部门名', l.city '城市名'
from departments d join locations l
on d.location_id = l.location_id
where d.department_id > 100;

-- 3.添加分组
#案例:查询每个城市的部门个数
select l.city '城市', count(*) '部门个数'
from departments d join locations l
on d.location_id = l.location_id 
group by l.city;

-- 4.添加分组+排序
#案例:查询部门中员工个数>10的部门名,并按员工个数降序
select d.department_name '部门名' , count(*) '员工个数'
from employees e join departments d
on e.department_id = d.department_id
group by d.department_name
having count(*)>10
order by count(*) desc;


##########非等值连接
#案例:查询部门编号在10-90之间的员工的工资级别,并按级别进行分组
select g.grade_level , count(*)
from employees e join job_grades g
on e.salary between g.lowest_sal and g.highest_sal
where e.department_id between 10 and 90
group by g.grade_level;

##########自连接
#案例:查询员工名和对应的领导名
select e.first_name '员工'  , m.first_name '领导'
from employees e join employees m
on e.manager_id = m.employee_id;

##########二.外连接##########
/*
		2.SQL99标准语法
			select 查询列表 
			from 表1 left | right | full [outer] join 表2
			on 连接条件
			where 筛选条件
			group by 分组字段  having 分组条件
			order by 排序字段;
*/
##########左外连接
##########右外连接
#案例1:查询所有女神记录,以及对应的男神名,如果没有对应的男神,则显示为null
-- 左连接
select g.* , b.*
from girl g left join boy b
on g.boyfriend_id = b.id;

-- 右连接
select g.* , b.*
from boy b right join girl g
on g.boyfriend_id = b.id;

#案例2:查哪个女神没有男朋友
-- 左连接
select g.*
from girl g left join boy b
on g.boyfriend_id = b.id
where b.name is null;

-- 右连接
select g.* , b.*
from boy b right join girl g
on g.boyfriend_id = b.id
where b.name is null;

#案例3:查哪个男神没有女朋友
-- 左连接
select b.*
from boy b left join girl g
on b.id = g.boyfriend_id
where g.name is null;

-- 右连接
select b.*
from girl g right join boy b 
on b.id = g.boyfriend_id
where g.name is null;

#案例4:查询哪个部门没有员工,并显示其部门编号和部门名
select d.department_id , d.department_name
from employees e right join departments d
on e.department_id = d.department_id
where e.first_name is null;

#案例5:查询城市名包含a字符的哪个城市没有部门,并按城市名降序
select l.city
from departments d right join locations l
on d.location_id = l.location_id
where d.department_name is null and l.city like '%a%'
order by l.city desc;

########## 全连接  MySQL不支持全连接,Oracle支持全连接 ##########
select count(*) from girl , boy; -- 13*7=91  交叉连接
select count(*) from girl g full join boy b on g.boyfriend_id = b.id; -- 13+7=20
select count(*) from girl g full join employees e on 1=2;   -- 13+107=120

##########三.交叉连接##########
select count(*) from girl , boy; -- 13*7=91  SQL92标准
select count(*) from girl cross join boy;  -- 13*7=91  SQL99标准

##########四.自然连接##########
-- 两张连接的表中字段名称和类型完全一致的列作为条件   数据一致
select girl.* , boy.* from girl NATURAL JOIN boy;
select employees.* , departments.* from employees NATURAL JOIN departments;


##########五.作业
########内连接########
-- 1. 显示所有员工的姓名,部门号和部门名称。
select e.first_name , d.department_id , d.department_name
from employees e join departments d
on e.department_id = d.department_id;

-- 2. 查询 90 号部门员工的 job_id 和 90 号部门的 location_id
select e.first_name , e.job_id , d.department_id, d.department_name , d.location_id
from employees e join departments d
on e.department_id = d.department_id
where d.department_id = 90;

-- 3. 选择所有有奖金的员工的last_name , department_name , location_id , city
select e.last_name , d.department_name , l.location_id , l.city
from employees e join departments d join locations l
on e.department_id = d.department_id and d.location_id = l.location_id
where e.commission_pct is not null;

-- 4. 选择city在Toronto工作的员工的last_name , job_id , department_id , department_name 
select e.last_name , e.job_id ,  d.department_name , l.city
from employees e join departments d on e.department_id = d.department_id
join locations l on d.location_id = l.location_id
where l.city = 'Toronto';

-- 5.查询每个工种、每个部门的部门名、工种名和最低工资
select d.department_name , j.job_title , MIN(salary) as '最低工资'
from employees e join departments d on e.department_id=d.department_id
join jobs j on e.job_id= j.job_id
GROUP BY d.department_name , j.job_title;

-- 6.查询每个国家下的部门个数大于 2 的国家编号
SELECT l.country_id
FROM departments d JOIN locations l
ON d.location_id = l.location_id
GROUP BY l.country_id
HAVING COUNT(*)>2;

-- 7、选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
-- employees Emp# manager Mgr#
-- kochhar 101 king 100
SELECT e.first_name 'employees', e.employee_id 'Emp#',  m.first_name 'manager', m.employee_id 'Mgr#' 
FROM employees e JOIN employees m
ON e.manager_id = m.employee_id;


########外连接########
-- 一、查询编号>3 的女神的男朋友信息,如果有则列出详细,如果没有,用 null 填充
SELECT g.*,b.*
FROM girl g LEFT JOIN boy b
ON g.boyfriend_id = b.id
WHERE g.id>3;

-- 二、查询哪个城市没有部门
SELECT l.city
FROM departments d RIGHT JOIN locations l
ON d.location_id = l.location_id
WHERE d.department_id IS NULL;

-- 三、查询部门名为 SAL 或 IT 的员工信息
SELECT e.first_name , d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_name = 'SAL' OR d.department_name = 'IT';


########select语句-02题目版########
-- 等值连接
-- 例子52:列出员工名字和部门名字?
SELECT e.first_name,d.department_name
FROM employees e join departments d
on e.department_id=d.department_id;
-- 例子53:列出部门号、部门名称、城市名称?
SELECT d.department_id,d.department_name,l.city
FROM departments d join locations l
ON d.location_id=l.location_id;
-- 例子54:列出Seattle城市有多少个部门?
SELECT count(*)
FROM departments d join locations l
ON d.location_id=l.location_id
where l.city='Seattle';
-- 例子55:列出Steven在哪个城市上班?
SELECT l.city
FROM departments d join employees e on e.department_id=d.department_id
join locations l on d.location_id=l.location_id
WHERE e.first_name='Steven';
-- 例子56:列出Seattle城市有多少员工?
SELECT count(*)
FROM departments d join employees e on e.department_id=d.department_id
join locations l on d.location_id=l.location_id
WHERE l.city='Seattle';

-- 例子57:列出Oxford城市营销部门Sal有那些员工?
SELECT  e.first_name
FROM employees e join departments d join locations l
on e.department_id = d.department_id AND d.location_id = l.location_id 
where l.city = 'Oxford' AND d.department_name = 'Sal';

SELECT  e.first_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 = 'Oxford' AND d.department_name = 'Sal';

-- 自连接
-- 例子58:列出员工名称和领导名称的对应关系?
SELECT e.first_name '员工', m.first_name '领导'
FROM employees e JOIN employees m
ON e.manager_id = m.employee_id;

-- 例子59:Diana的领导是谁?
SELECT m.first_name
FROM employees e JOIN employees m 
ON e.manager_id = m.employee_id
WHERE e.first_name = 'Diana';

-- 例子60:Steven是谁的领导?
SELECT e.first_name
FROM employees e JOIN employees m 
ON e.manager_id = m.employee_id
WHERE m.first_name = 'Steven';

-- 例子61:请问哪些员工是领导?
select DISTINCT m.first_name
from employees e join employees m
on e.manager_id = m.employee_id;

-- 外连接
-- 例子62:列出员工和领导的对应关系,包含‘Steven’
SELECT e.first_name '员工', ifnull(m.first_name,'Boss') '领导'
FROM employees e left JOIN employees m
ON e.manager_id = m.employee_id;

-- 例子63:列出哪些人是员工?(或者)哪些员工不是领导? 18 + 89 = 107
select m.first_name '纯员工'
from employees e right join employees m
on e.manager_id = m.employee_id
where e.first_name is null;

##########子查询##########
/*
	一.按子查询出现的位置进行分类:
​		1、select后面
​		要求:子查询的结果为单行单列(标量子查询)
​		2、from后面
​		要求:子查询的结果可以为多行多列
​		3、where或having后面
​		要求:子查询的结果必须为单列;单行子查询;多行子查询
​		4、exists后面
​		要求:子查询结果必须为单列(相关子查询)

	二.其中where或having后面是重点:
​	单行子查询
​			特点:子查询的结果集只有一行一列  
			符号:>  <  >=  <=  =  <>

​	多行子查询
​			特点:子查询的结果集有多行一列
			符号:in 、any 、all 、not in
*/
##########三.案例##########
-- 一、放在where或having后面
-- 1.单行子查询   单行单列
#案例1:谁的工资比 Abel 高?
-- 步骤1:
select salary from employees where last_name='Abel';
-- 步骤2:
select first_name , salary 
from employees 
where salary > (select salary from employees where last_name='Abel');

#案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id 和工资
-- 步骤1:
select job_id from employees where employee_id = 141;
-- 步骤2:
select salary from employees where employee_id = 143;
-- 步骤3:
select first_name , job_id , salary
from employees
where job_id = (select job_id from employees where employee_id = 141)
and salary > (select salary from employees where employee_id = 143);

#案例3:返回公司工资最少的员工的last_name,job_id和salary
-- 步骤1:
select min(salary) from employees;
-- 步骤2:
select last_name , job_id , salary
from employees
where salary = (select min(salary) from employees);

#案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资
-- 步骤1:
select min(salary) from employees group by department_id having department_id=50;
-- 步骤2:
select min(salary) from employees group by department_id;
-- 步骤3:
select department_id , min(salary) 
from employees 
group by department_id
HAVING min(salary) > (select min(salary) from employees group by department_id having department_id=50);

-- 2.多行子查询  多行单列
#案例1:返回location_id是1400或1700的部门中的所有员工姓名
-- 步骤1:
select department_id from departments where location_id in(1400,1700);
-- 步骤2:
select first_name 
from employees
where department_id in (select department_id from departments where location_id in(1400,1700));

#案例2:返回其它部门中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
-- 步骤1:
select salary from employees where job_id='IT_PROG';
-- 步骤2:
select employee_id , first_name , job_id , salary
from employees
where salary 10000的员工姓名
-- 步骤1:
select e.first_name 
from employees e join departments d join locations l 
on e.department_id = d.department_id and d.location_id = l.location_id
where l.city = 'Seattle';
-- 步骤2:
select first_name , salary
from employees
where first_name in(
	select e.first_name 
	from employees e join departments d join locations l 
	on e.department_id = d.department_id and d.location_id = l.location_id
	where l.city = 'Seattle'
) and salary>10000;

-- 二、放在select后面
#案例:查询部门编号是50的员工个数
select (select count(*) from employees where department_id=50) '员工个数';

-- 三、放在from后面    子查询的结果可以为多行多列
#案例:查询每个部门的平均工资的工资级别
-- 步骤1:先查询各个部门的平均工资
select department_id , avg(salary) from employees group by department_id;
-- 步骤2:
select s.department_id , s.sal , g.grade_level
from (select department_id , avg(salary) sal from employees group by department_id) s join job_grades g
on s.sal between g.lowest_sal and g.highest_sal;

-- 四、放在exists后面
#案例1 :查询有无名字叫“Abel”的员工信息
-- 步骤1:
select last_name from employees where last_name = 'Abel';
-- 步骤2: EXISTS返回1代表存在,返回0则代表不存在
select EXISTS(select last_name from employees where last_name = 'Abel');

#案例2:查询没有女朋友的男神信息
-- 步骤1:
select b.name
from girl g RIGHT JOIN boy b
on g.boyfriend_id = b.id
where g.name is null;

-- 步骤2:
select b.*
from boy b
where EXISTS(select b.id from girl g where g.boyfriend_id = b.id)=0;

select b.*
from boy b
where not EXISTS(select b.id from girl g where g.boyfriend_id = b.id);

##########四.作业##########
-- 1. 查询和 Zlotkey 相同部门的员工姓名和工资
SELECT department_id FROM employees where last_name = 'Zlotkey';

SELECT last_name,salary 
from employees 
where department_id = (SELECT department_id FROM employees where last_name = 'Zlotkey');

-- 2. 查询工资比公司平均工资高的员工的员工号,姓名和工资。
SELECT AVG(salary) from employees;

SELECT employee_id,last_name,salary
FROM employees
where salary > (SELECT AVG(salary) from employees );

-- 3. 查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资
select department_id , avg(salary) from employees group by department_id;

select e.employee_id , e.first_name , e.salary , e.department_id
from employees e ,(select department_id , avg(salary) a from employees group by department_id) s
where e.department_id = s.department_id and e.salary > s.a
order by e.department_id;

-- 4. 查询和姓名中包含字母 u 的员工在相同部门的员工的员工号和姓名
SELECT employee_id,first_name
FROM employees
WHERE department_id IN(SELECT department_id FROM employees WHERE first_name LIKE '%u%');

-- 5. 查询在部门的 location_id 为 1700 的部门工作的员工的员工号
SELECT employee_id
FROM employees
WHERE department_id IN(SELECT department_id FROM departments WHERE location_id=1700);

-- 6. 查询管理者是 King 的员工姓名和工资
SELECT first_name,salary
FROM employees
WHERE manager_id IN(SELECT employee_id FROM employees WHERE last_name='K_ing');

-- 7. 查询工资最高的员工的姓名,要求 first_name 和 last_name 显示为一列,列名为 姓.名
SELECT CONCAT(first_name,'.',last_name) '姓.名'
FROM employees
WHERE salary =(SELECT MAX(salary) FROM employees);

########select语句-02题目版########
-- 1.单列单行  
-- 例子65.求出谁的工资最低?
SELECT *
FROM employees
ORDER BY salary
LIMIT 1;

select * 
from employees
where salary = (select min(salary) from employees);

-- 例子66.谁和"Neena"坐同一个职位?
SELECT first_name
FROM employees
WHERE job_id =(SELECT job_id FROM employees WHERE first_name='Neena');

-- 例子67.哪些部门的平均工资比30部门的平均工资高?
SELECT department_id,AVG(salary)
FROM employees
GROUP BY department_id
HAVING AVG(salary)>(SELECT AVG(salary) FROM employees WHERE department_id=30);

-- 例子68.找出与 ‘Steven’ 同部门的员工信息?
SELECT employee_id,first_name,department_id
FROM employees
WHERE department_id IN(SELECT department_id FROM employees WHERE first_name='Steven');

-- 例子69.找出比 ‘Susan’ 早入职的员工?
SELECT employee_id,first_name,hiredate
FROM employees
WHERE hiredate<(SELECT hiredate FROM employees WHERE first_name='Susan');

-- 2.单列多行
-- 例子70.哪些人是领导?
select DISTINCT manager_id from employees where manager_id is not null;

select * 
from employees
where employee_id in (select DISTINCT manager_id from employees where manager_id is not null);


-- 例子71.哪些部门有员工?
select department_name
from departments d
where EXISTS(
select * from employees e where e.department_id=d.department_id);

select DISTINCT department_id from employees where department_id is not null;

select department_name
from departments 
where department_id in (select DISTINCT department_id from employees where department_id is not null);


-- 例子72.哪些部门没有员工?
select department_name
from departments d
where not EXISTS(
select * from employees e where e.department_id=d.department_id);


select DISTINCT department_id from employees where department_id is not null;

select department_name
from departments 
where department_id not in (select DISTINCT department_id from employees where department_id is not null);

-- 例子73.哪些人是员工?(哪些人不是领导)
select DISTINCT manager_id from employees where manager_id is not null;

select * 
from employees
where employee_id not in (select DISTINCT manager_id from employees where manager_id is not null);


-- 例子74.查询和50部门员工职位相同的所有员工的姓名?
select DISTINCT job_id from employees e where department_id=50;

select first_name
from employees
where job_id in(select DISTINCT job_id from employees e where department_id=50);

-- 3.多列子查询
-- 例子75.哪些员工的工资和本部门的平均工资一致?
select department_id , AVG(salary) from employees GROUP BY department_id;

select e.first_name , e.department_id , e.salary
from employees e , (select department_id , AVG(salary) a from employees GROUP BY department_id)s
where e.department_id = s.department_id and e.salary = s.a;

-- 例子76.哪些员工是在Seattle工作,请打印出全名,部门名,薪资?
select location_id from locations where city = 'Seattle';
select * from departments where location_id = (select location_id from locations where city = 'Seattle');

select concat(e.first_name,e.last_name) name , d.department_name , e.salary
from employees e , 
(select * from departments where location_id = (select location_id from locations where city = 'Seattle')) d
where e.department_id = d.department_id;

##########分页查询##########
/*
	一.语法
	select 查询列表
	from 表1 [left | right |full][inner | outer] join 表2
	on 连接条件
	where 筛选条件
	group by 分组列表
	having 分组条件
	order by 排序列表 [asc | desc]
	limit 起始值 , 每页展示记录数;
	
	二.执行顺序
	from -> join -> on -> where -> group by -> having -> select -> order by -> limit
	
	三.特点:
	①起始条目索引如果不写,默认是0
​	②limit后面支持两个参数 参数1:显示的起始条目索引 参数2:条目数
*/
/*
	关于分页
	
	页面中会提供的数据
	1.当前页  pageNow
	2.上一页  pageNow-1
	3.下一页  pageNow+1
	
	数据库中提供的数据
	1.总记录数 myCounts
	2.展示数据 list  
	
	计算
	1.总页数  myPages
  2.起始值  begin	
	
	规定每页展示记录数12条 size
*/
-- 查询employees表中总记录数  myCounts
-- select count(*) from employees where 筛选条件;

-- 计算总页数
-- myPages = myCounts%size==0 ? myCounts/size : myCounts/size+1

-- 查询数据  list
-- select * from employees where 筛选条件;

-- 计算起始值 begin
/*
		1    			   0 
	  2    			  12
    3    			  24
	 pageNow    (pageNow-1)*size
*/
-- begin = (pageNow-1)*size


############四.案例#############
#案例1:查询员工信息表的前5条
select * from employees limit 0 , 5;
select * from employees limit 5;

#案例2:查询第11条——第20条的员工信息
-- 第二页
select * from employees limit 11 , 10;

#案例3:查询有奖金的工资最高的前三名员工名、工资、奖金、部门编号
select first_name , salary , commission_pct , department_id
from employees
where commission_pct is not null
order by salary desc
limit 3;

#案例4:查询年薪最高的前10名
select first_name , salary , salary*12
from employees
order by salary*12 desc
limit 10;

#案例5:使用分页查询实现,查询员工信息表中部门为50号的工资最低的5名员工信息
select first_name , salary , department_id
from employees
where department_id = 50
order by salary
limit 5;

#案例6:查询员工表中薪资排第二的员工信息
select first_name , salary
from employees
order by salary desc
limit 1,1;

############五.作业#############
-- 1. 查询工资最低的员工信息: last_name, salary
select * from employees order by salary limit 1;

-- 2. 查询平均工资最低的部门信息
select d.*
from employees e join departments d
on e.department_id = d.department_id
group by department_id
limit 1;

-- 3. 查询平均工资最低的部门信息和该部门的平均工资
select d.* , avg(salary)
from employees e join departments d
on e.department_id = d.department_id
group by department_id
limit 1;

-- 4. 查询平均工资最高的 job 信息
select j.* , avg(salary)
from employees e join jobs j
on e.job_id = j.job_id
group by e.job_id
order by avg(salary) desc
limit 1;

-- 5. 查询平均工资高于公司平均工资的部门有哪些?
select department_id , avg(salary) from employees group by department_id;
select avg(salary) from employees;

select s1.department_id
from (select department_id , avg(salary)a from employees group by department_id) s1 , 
(select avg(salary)a from employees) s2
where s1.a>s2.a;

-- 6. 查询出公司中所有 manager 的详细信息.
select DISTINCT m.* 
from employees e right join employees m
on e.manager_id = m.employee_id 
where e.employee_id is not null;

-- 7. 各个部门中 最高工资中最低的那个部门的 最低工资是多少
select department_id , max(salary)
from employees
group by department_id
order by max(salary)
limit 1;

-- 8. 查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
select department_id
from employees
group by department_id
order by avg(salary) desc
limit 1; -- department_id 90

select manager_id
from departments 
where department_id = (select department_id from employees group by department_id order by avg(salary) desc limit 1); -- manager_id 100

select employee_id , last_name, department_id, email, salary
from employees e
where e.employee_id = (select manager_id
from departments 
where department_id = (select department_id
from employees
group by department_id
order by avg(salary) desc
limit 1));


-- TopN查询:
-- 例子77:请找出公司中工资前5的员工的信息?
select *
from employees
order by salary desc
limit 5;

-- 分页
-- 例子78:请找出所有员工工资由高到低中,第3条到第10条员工的全名和工资信息?
select CONCAT(first_name , last_name) name, salary
from employees
order by salary desc
limit 2 , 8;

############联合查询#############
/*
	一.语法
	select 查询列表 from 表1  where 筛选条件  
	union / union all
	select 查询列表 from 表2  where 筛选条件  

	二.特点
	1、多条待联合的查询语句的查询列数必须一致,查询类型、字段意义最好一致
	2、union实现去重查询
     union all 实现全部查询,包含重复项
*/
############案例#############
#案例1:查询所有国家的年龄>20岁的用户信息
select * from chinese where age>20
UNION
select * from japanese where jage>20
UNION
select * from usa where uage>20;

#案例2:查询所有国家的用户姓名和年龄
select name , age from chinese
UNION
select jname , jage from japanese
UNION
select uname , uage from usa;

#案例3:union自动去重/union all 可以支持重复项
select name , age from chinese
UNION ALL
select jname , jage from japanese
UNION ALL
select uname , uage from usa;

############作业#############
#作业1:查询出所有女神及男神信息 
select * from girl g left join boy b on g.boyfriend_id = b.id
UNION
select * from girl g right join boy b on g.boyfriend_id = b.id;

#作业2:查询出没有男朋友的女神信息和没有女朋友的男神信息
select * from girl g left join boy b on g.boyfriend_id = b.id where b.name is null
UNION
select * from girl g right join boy b on g.boyfriend_id = b.id where g.name is null;

#作业3:查询出所有员工及部门信息
select * from employees e left join departments d on e.department_id = d.department_id
UNION
select * from employees e right join departments d on e.department_id = d.department_id;

#作业4:查询出没有部门的员工信息及没有员工的部门信息
select * from employees e left join departments d on e.department_id = d.department_id where d.department_name is null
UNION
select * from employees e right join departments d on e.department_id = d.department_id where e.first_name is null;

#作业5:查询出所有部门及区域信息
select * from departments d left join locations l on d.location_id = l.location_id
UNION
select * from departments d right join locations l on d.location_id = l.location_id;

#作业6:查询出没有城市的部门及没有部门的城市信息
select * from departments d left join locations l on d.location_id = l.location_id where l.city is null
UNION
select * from departments d right join locations l on d.location_id = l.location_id where d.department_name is null;


############第一部分############
-- 1.查询和Steven相同部门的员工姓名和入职日期AD_PRES ST_CLERK

select job_id from employees where first_name='Steven';

select e.first_name,s.job_id
from employees e join (select job_id from employees where first_name='Steven') s
on e.job_id=s.job_id

-- 2.查询工资比公司平均工资高的员工的员工号,姓名和工资。
SELECT avg(salary) from employees 

select e.employee_id,e.first_name,salary from employees e where salary>(SELECT avg(salary) from employees);

-- 3.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资

select AVG(salary) from employees GROUP BY department_id;

select e.employee_id,e.first_name,e.department_id,salary
from employees e join (select department_id,AVG(salary) avgSal from employees GROUP BY department_id) s
on e.department_id=s.department_id
where e.salary>s.avgSal;

-- 4.查询姓名中包含字母u的员工在相同部门的员工的员工号和姓名
select DISTINCT department_id from employees where first_name like '%u%';
select employee_id,first_name from employees where department_id in(select DISTINCT department_id from employees where first_name like '%u%');
-- 5. 查询在location_id为1700的区域的部门工作的员工的员工号
select department_id from departments where location_id=1700

select employee_id,first_name from employees where department_id in(select department_id from departments where location_id=1700);
-- 6. 查询Neena的手下员工姓名和工资
select * from employees e right join employees m on e.manager_id=m.employee_id where m.first_name='Neena'

select employee_id from employees where first_name='Neena'

SELECT first_name,salary
from employees 
where manager_id=(select employee_id from employees where first_name='Neena');
############第二部分############
-- 1.查询年薪大于120000的员工姓名和工资
select first_name,salary from employees where salary*12>120000;
-- 2.查询员工号为174的员工的姓名和部门名称
select first_name,department_id from employees where employee_id=174;
-- 3.选择工资不在5000到12000的员工的姓名和工资
select * from employees where salary not BETWEEN 5000 and 12000;
-- 4.选择入职时间在2014-03-01到2014-10-31之间的员工姓名,job_id和入职时间
select * from employees where hiredate  BETWEEN '2014-3-1' and '2014-10-31';
-- 5.选择在20或50号部门工作的员工姓名和部门名称
select * from employees where department_id in(20,50) ORDER BY department_id;
-- 6.选择在1992年入职的员工的姓名和入职时间
select * from employees where YEAR(hiredate)=1992
-- 7.选择公司中没有领导的员工姓名及job_id
select * from employees where manager_id is null;

-- 8.选择公司中有奖金的员工姓名,工资和奖金
select * from employees where commission_pct is not null;
-- 9.选择员工姓名的第三个字母是d的员工姓名

-- 10.选择姓名中有字母a和e的员工姓名
select last_name from employees where last_name like '%a%' and last_name like '%e%';
-- 11.显示2021年3月17日是星期几 ??
 SELECT WEEKDAY('2022-12-6')+1
-- 12.查询员工号,姓名,工资,以及工资提高百分之20%后的结果(new salary)
select  employee_id,first_name,salary*(1+0.2) 'new salary' from employees 

-- 13.将员工的姓名按首字母排序,并写出姓名的长度(length)
select  first_name  ,LENGTH(first_name) '长度',CHAR_LENGTH(first_name) '个数'from employees ORDER BY first_name

-- 14.查询员工的姓名,并显示出各员工在公司工作的月份数
SELECT first_name,TIMESTAMPDIFF(year,hiredate,now()) from employees
SELECT first_name,TIMESTAMPDIFF(month,hiredate,now()) from employees
SELECT first_name,TIMESTAMPDIFF(day,hiredate,now()) from employees
SELECT first_name,TIMESTAMPDIFF(week,hiredate,now()) from employees

SELECT first_name,TIMESTAMPDIFF(week,'2000-4-3',now()) from employees

-- 15.查询员工的姓名,以及在公司工作的月份数(worked_month),并按工作的月份数降序排列
SELECT first_name,TIMESTAMPDIFF(month,hiredate,now()) '月份数' from employees ORDER BY TIMESTAMPDIFF(month,hiredate,now())




-- 16.使用case函数,按照下面的条件:
-- job_id             level
-- AD_PRES              A
-- AD_VP     						B
-- IT_PROG        			C
-- ST_CLERK             D
-- Others           		E
-- 产生下面的结果,并按照level排序
-- first_name	   job_id	   level
-- Steven	       AD_PRES	   A

select first_name,job_id,
(
	case job_id 
	when 'AD_PRES' then 'A'
	when 'AD_VP' 		then 'B'
	when 'IT_PROG' then 'C'
	when 'ST_CLERK' then 'D'
	else 'E'
	end
) level
from employees


select first_name,job_id,
(
	case 
	when job_id='AD_PRES' then 'A'
	when job_id='AD_VP' 		then 'B'
	when job_id='IT_PROG' then 'C'
	when job_id='ST_CLERK' then 'D'
	else 'E'
	end
) level
from employees



-- 17.查询各job_id的员工工资的最大值,最小值,平均值,总和
select job_id,max(salary),MIN(salary),AVG(salary),SUM(salary)
from employees
GROUP BY job_id

-- 18.选择具有各个job_id的员工人数
select job_id,count(*) from employees GROUP BY job_id
-- 19.查询各个经理手下员工的最低工资,其中最低工资不能低于1000,没有经理的员工不计算在内

select MIN(salary),manager_id
from employees
where manager_id is not null
GROUP BY manager_id
HAVING min(salary)>=1000

-- 20.查询所有部门的名字,location_id,员工数量和工资平均值
select d.department_name,d.location_id,count(*),AVG(salary)
from employees e join departments d
on e.department_id=d.department_id
GROUP BY e.department_id




2.2**DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据) insert / update / delete**

-- DDL:Data Define Language 数据定义语言,用于对数据库和表的管理和操作
######### 库的管理 #########
#查看所有数据库
show databases;

#查询当前正在使用的数据库
select database();

#切换数据库
use testshop;

#创建数据库
#语法:create database if not EXISTS 数据库库名;
create database if not EXISTS testshop;

#删除数据库
#语法:drop database if EXISTS 数据库库名;
drop database if EXISTS testshop;

#######数据类型#######

#######一.数值型#######
/*
	1.int:整型,默认长度是11,int(11),此处的11指的是出现数据的长度
  2.double/float:浮点数类型,例如double(5,2)表示最多5位,其中必须有2位小数,即最大值为999.99;
  3.decimal:定点数类型,在表示钱方面使用该类型,因为不会出现精度缺失问题;
	
	二.注意:
	①.浮点数和定点数都可以用类型名称后加“(M,D)”的方式来表示。
	M:精度 ,该值的整数位+小数位一共显示M位数字
	D:标度, 小数位数一共显示D位数字,如果不够后面用0补齐,如果超过,则四舍五入

	2.浮点数和定点数的区别:
	a) 定点数在MySQL内部以字符串形式存放,比浮点数更精确,适合用于表示货币等精度高的数据;
	b) 在不指定精度时,浮点数默认会按照实际的精度来显示,而定点数在不指定精度时,默认M=10,D=0;
	
	三.使用场景
	int类型:整型,自增的主键、年龄、数量等等
	double类型:浮点型,薪资、提成、成绩等等
	decimal类型:定点型,涉及到小数点有效位数,精确度高的货币,钱等等
*/
#删除表
#语法:drop table if EXISTS 表名;
#创建表
#语法:create table if not EXISTS 表名( 字段名  字段类型,.... );
drop table if EXISTS test;
create table if not EXISTS test(
	c1 int(11),       -- 整型 默认int(11) ,此处11为允许写的数值长度  int占4个字节
	c2 double(5,2),   -- 浮点型 有效位数是5,小数点后2位,即最大值为999.99  四舍五入
	c3 float(6,2),	  -- 浮点型 有效位数是6,小数点后2位  四舍五入
	c4 decimal(10,1)  -- 定点型 默认decimal(10,0) M=10,D=0 
);
#插入数据 DML操作
#语法:insert 表名  values(值,....);
-- 1264 - Out of range value for column 'c3' at row 1  99999.9 -> 99999.90
insert into test values(111 , 999.99 , 99999.9 , 88888.8);
-- 999.994 四舍五入 999.99
insert into test values(111 , 999.994 , 9999.99 , 88888.8);
-- 1264 - Out of range value for column 'c2' at row 1  999.996 -> 1000.00
insert into test values(111 , 999.996 , 9999.99 , 88888.8);
-- Out of range value for column 'c1' at row 1
-- insert into test values(1111111111122 , 999.99 , 9999.99 , 88888.8);
insert into test values(1111111111, 999.99 , 9999.99 , 88888.8);

select * from test;

#######二.字符型#######
/*
	char:固定长度字符串类型;char(n)  n范围是0-255之间的整数
	varchar:可变长度字符串类型;varchar(n) n范围是0~65535之间的整数
	text:字符串类型;表示存储较长文本
	
	区别:
	char
	1.固定长度字符串类型;建表时指定多长实际占多长
	2.可以声明字段时,不指定长度,默认char(1)
	3.char(n)  n范围是0-255之间的整数
	4.插入数据时,末尾有空格,会自动去除默认空格
	5.场景:性别、标志等
	
	varchar
	1.可变长度字符串类型;会根据实际插入的数据长度
	2.声明字段时,必须指定长度
	3.varchar(n) n范围是0~65535之间的整数
	4.插入数据时,末尾有空格,不会自动去除默认空格
	5.场景:姓名、住址、手机号等等
	
	text
	1.字符串类型;表示存储较长文本
	2.场景:存储描述、评价、评论等
*/
drop table if EXISTS test;
create table if not EXISTS test(
	c1 char(9),
	c2 varchar(9),
	c3 text
);
-- 插入数据
insert into test values('   abc   ','   abc   ','aaabbbccc');
insert into test values('abc','abc','aaabbbccc');
insert into test values('abc   ','abc   ','aaabbbccc');

-- 验证,对末尾空格敏感度
select concat('###',c1,'###') , concat('###',c2,'###') , c3 from test;
-- 获取字符长度
select length(c1) , length(c2) from test;

#######三.时间类型#######
/*
	date:日期类型,格式为:yyyy-MM-dd
	time:时间类型,格式为:hh:mm:ss
	datetime: 日期+时间 格式为:yyyy-MM-dd hh:mm:ss 或者 yyyyMMddhhmmss
	timestamp:时间戳类型;(除非自己写入数据,否则将直接获取当前平台时间进行写入)

	二.使用场景:
	date类型:日期,生日、入职日期等;
	datetime类型:时间与日期,商品入库时间、生产日期等
*/
drop table if EXISTS test;
create table if not EXISTS test(
	c1 date,
	c2 time,
	c3 datetime,
  c4 timestamp
);
-- 插入数据
insert into test(c1) values('2014-10-10');
insert into test(c2) values('15:15:15');
insert into test(c3) values(20221010141414); -- yyyyMMddhhmmss
insert into test(c3) values('2022-12-12 15:15:15'); -- yyyy-MM-dd hh:mm:ss
insert into test(c4) values(20221010161616); -- yyyyMMddhhmmss
insert into test values('2022-10-10','16:16:16',now(),null);

select * from test;

#######四.json类型#######
#创建表
drop table if EXISTS test;
create table if not EXISTS test(
	id int comment '编号',
	name varchar(255) comment '姓名',
	age int comment '年龄',
	gender char(1) comment '性别',
	login_info json DEFAULT null comment '登录方式json格式,有phone,wechat,alipay,qq,email'
);
#######插入数据#######
insert into test values(1,'Jack',18,'男','{"phone":"13911223433" , "wechat":"admin123"}');
insert into test values(2,'Rose',19,'女','{"phone":"13911223444" , "qq":"123456"}');
insert into test values(3,'Sun',20,'男','{"phone":"13911223455" , "wechat":"admin123" , "email":"[email protected]"}');
insert into test values(4,'Sun',24,'女','{"wechat":"admin123"}');

#######查询数据#######
-- 案例1:查询列login_info的json串中的键为phone的值
-- JSON_EXTRACT(json_doc, path[, path] ...):从json中返回想要的字段
-- 用法:JSON_EXTRACT(json格式数据, '$.json字段名',...)
select JSON_EXTRACT(login_info, '$.phone') phone from test;
-- 用法:简化写法,通过 json格式数据 -> '$.json字段名'
select login_info -> '$.phone' phone from test;

-- 案例2:查询列login_info的json串中的键为phone的值
-- JSON_UNQUOTE(json_val):去除json字符串的双引号,将值转成string类型
-- 用法:JSON_UNQUOTE(JSON_EXTRACT(json格式数据, '$.json字段名',...))
select JSON_UNQUOTE(JSON_EXTRACT(login_info, '$.phone')) phone from test where id = 1;
-- 用法:简化写法,通过 json格式数据 ->> '$.json字段名'
select login_info ->> '$.phone' phone from test;

-- 案例3:查询列login_info的json串中的键为phone、键为wechat的值
-- 简化写法
select
login_info ->> '$.wechat' wechat , login_info ->> '$.phone' phone 
from test;

#使用JSON中的字段作为查询条件
-- 案例:根据指定键phone的数据,查询用户信息
select name, gender 
from test
where login_info ->> '$.phone' = '13911223433';

-- 或者
-- JSON_CONTAINS(target, candidate[, path]) JSON格式数据是否在字段中包含特定对象
-- 用法:JSON_CONTAINS(json格式数据, '"数据"', '$.json字段名')
select name, gender 
from test
where JSON_CONTAINS(login_info, '"13911223433"' , '$.phone');

-- 或者
-- JSON_OBJECT([key, val[, key, val] ...]) 将一个键值对列表转换成json对象
-- 用法:JSON_OBJECT('键','值')
select name, gender 
from test
where JSON_CONTAINS(login_info, JSON_OBJECT('phone' , '13911223433'));

#JSON_PRETTY 查询格式化的json数据
-- 案例:查询t_user表中的json数据,要求以json格式输出
-- JSON_PRETTY(json_val) 查询格式化的json数据
-- 用法:JSON_PRETTY(json格式数据)
select JSON_PRETTY(login_info) from test where id = 1;


select * from test where name like 'Sun%';
select * from test where length(name)=4;
#更新数据的语法 update 表名 set 字段=值 where 条件;

#######更新数据:修改JSON串中指定字段的值#######
-- 案例1:将列login_info中的json串中的键为phone的值设置为‘13600001122’
-- JSON_SET(json_doc, path, val[, path, val] ...) 设置JSON串中的字段值,若有则修改,若无则添加
-- 用法:JSON_SET(json格式数据, json中字段, 值,...)
update test set login_info=JSON_SET(login_info, '$.phone', '13600001122') where name like 'Sun%';

-- 案例2:将列login_info中的json串中的键为phone的值替换为‘13600001133’
-- JSON_REPLACE(json_doc, path, val[, path, val] ...) 替换JSON串中的字段值,若有则替换,若无则不操作
-- 用法:JSON_REPLACE(json格式数据, json中字段, 值,...)
update test set login_info=JSON_REPLACE(login_info, '$.wechat', '112233') where length(name)=4;

#######删除数据#######
-- 案例:将列login_info中的json串中的键为wechat的值删除
-- JSON_REMOVE(json_doc, path[, path] ...) 从JSON串中删除数据
-- 用法:JSON_REMOVE(json格式数据 ,json中字段 )
update test set login_info=JSON_REMOVE(login_info, '$.wechat');
select * from test;

#######json类型 - JSON数组类型#######
#建表
create table if not EXISTS t_product_tag(
	product_id int,
	address json DEFAULT null,
	tag_id json DEFAULT null
);
desc t_product_tag;
#插入数据
insert into t_product_tag values(1 , '{"address":{"sheng":"江苏省" , "shi":"无锡" , "qu":"新吴区"}}' ,   '[1,2,3]');
insert into t_product_tag values(2 , '{"address":{"sheng":"江苏省" , "shi":"南京" , "qu":"雨花台区"}}' , '[7,8,9]');
insert into t_product_tag values(3 , '{"address":{"sheng":"江苏省" , "shi":"无锡" , "qu":"滨湖区"}}' ,   '[8,10,3]');

#查询数据
select * from t_product_tag;

#查询地址的市的信息
select address->>'$.address.qu' 区 from t_product_tag;

-- 1.MEMBER OF 搜索出JSON 文档中含有指定数据的元素
-- 用法:数值 MEMBER OF(json格式数据 -> '$')
select * from t_product_tag where 8 MEMBER OF(tag_id->'$');

-- 2.JSON_CONTAINS(json_doc1, json_doc2) 分别指定两个用于比较的 JSON 文档。要求搜索的数组的所有元素都存在于被搜索的数组中
-- 用法:JSON_CONTAINS(json格式数据 -> '$' , '[数据]') 对搜索键执行 AND 运算
select * from t_product_tag where JSON_CONTAINS(tag_id ->'$', '[7,8]');


-- 3.JSON_OVERLAPS(json_doc1, json_doc2) 分别指定两个用于比较的 JSON 文档。如果两个参数都是标量,则函数执行简单的相等性测试。
-- 用法:JSON_OVERLAPS(json格式数据 -> '$' , '[数据]') 执行 OR 运算
select * from t_product_tag where JSON_OVERLAPS(tag_id ->'$' , '[7,8]');

#######五.二进制类型#######
/*
	Blob:字节类型;可以用来存储图片数据jpg、 音乐mp3 、 视频avi。 
	在MySQL中Blob是一个二进制大型对象,可以是存储大量数据的容器,他能容纳不同大小的数据,
	插入Blob类型的数据必须使用PrepareStatement 因为Blob类型的数据无法使用字符串拼接。 
	
	TinyBlob255
	Blob65K
	MediumBlob16M
	LongBlob4G
	
	建议:使用存储资源至服务器的外链地址  OSS
*/
drop table if EXISTS test;
create table if not EXISTS test(
	c MediumBlob
);

select * from test;


#######json数据类型作业#######
CREATE TABLE `dept` (
`id` int(11) NOT NULL,
`dept` varchar(255) DEFAULT NULL,
`json_value` json DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into dept VALUES(1,'部门1','{"deptName": "部门1", "deptId": "1", "deptLeaderId": "3"}');
insert into dept VALUES(2,'部门2','{"deptName": "部门2", "deptId": "2", "deptLeaderId": "4"}');
insert into dept VALUES(3,'部门3','{"deptName": "部门3", "deptId": "3", "deptLeaderId": "5"}');
insert into dept VALUES(4,'部门4','{"deptName": "部门4", "deptId": "4", "deptLeaderId": "5"}');
insert into dept VALUES(5,'部门5','{"deptName": "部门5", "deptId": "5", "deptLeaderId": "5"}');

-- 1、使用 json字段名->’$.json属性’ 进行查询条件 , 查询deptLeaderId为5的数据
select * from dept where json_value->>'$.deptLeaderId'='5';

-- 2、查dept为“部门3”和deptLeaderId=5的数据
select * from dept where dept='部门3' and json_value->>'$.deptLeaderId'=5;

-- 3、查询json格式中deptLeaderId=5和deptId=5的数据
select * from dept where json_value->>'$.deptId'=5 and json_value->>'$.deptLeaderId'=5;

CREATE TABLE `dept_leader` (
`id` int(11) NOT NULL,
`leaderName` varchar(255) DEFAULT NULL,
`json_value` json DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 插入一些测试数据
insert into dept_leader VALUES(1,'leader1','{"name": "王一", "id": "1", "leaderId": "1"}');
insert into dept_leader VALUES(2,'leader2','{"name": "王二", "id": "2", "leaderId": "3"}');
insert into dept_leader VALUES(3,'leader3','{"name": "王三", "id": "3", "leaderId": "4"}');
insert into dept_leader VALUES(4,'leader4','{"name": "王四", "id": "4", "leaderId": "5"}');
insert into dept_leader VALUES(5,'leader5','{"name": "王五", "id": "5", "leaderId": "5"}');

-- 4、连表查询在dept 表中部门leader在dept_leader 中的详情
select d.json_value->>'$.deptLeaderId' , l.*
from dept d , dept_leader l
where d.json_value->>'$.deptLeaderId' = l.json_value->>'$.id';


###########表的管理##########

###########一.创建表##########
#创建表结构语法:create table if not EXISTS 表名(字段1 数据类型 , 字段2 数据类型,....);
create table if not EXISTS student(
	id int,
	name varchar(255),
	sex char(1),
	grade double(4,1),
	num varchar(10)
);

###########二.查看表##########
#查看表结构语法:desc 表名;
desc student;
-- 查看当前数据库中所有表名称
show tables;
-- 查看指定表的创建语句
show create table testddl.student;
-- 查询结果
CREATE TABLE `student` (
  `id` int DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  `grade` double(4,1) DEFAULT NULL,
  `num` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3

###########三.删除表##########
#删除表结构语法:drop table if EXISTS 表名;
drop table if EXISTS student;

###########四.修改表##########
-- 修改之 修改表名称RENAME:修改student表名称为stus:
-- 语法:alter table 表名 rename 新表名;
alter table student rename to stus;

desc stus;
-- 修改之 添加列ADD:给stus表添加classname列:
-- 语法:alter table 表名 add 新字段名称;
alter table stus add classname varchar(255);

-- 修改之 修改列名change:修改stus表的sex列名为gender:
-- 语法:alter table 表名 change  旧字段名  新字段名 数据类型;
alter table stus change sex gender char(1);
alter table stus change name name varchar(100);

-- 修改之 修改列类型MODIFY:修改stus表的gender列类型为CHAR(2):
-- 语法:alter table 表名 modify 字段名  新数据类型;
alter table stus modify gender char(2);

-- 修改之 删除列DROP:删除stus表的classname列:
-- 语法:alter table 表名 drop 字段名;
alter table stus drop classname;


###########五.复制表##########
-- 仅仅复制表的结构
-- 语法:CREATE TABLE 新表名 LIKE 表;  
-- 特点:不会携带数据,但是会携带约束条件主键、外键,自增长等
create table myemp like testdql.employees;
desc myemp;
select * from myemp;
drop table myemp;

-- 复制表的结构+数据
-- 语法:CREATE TABLE 新表名 SELECT * FROM 表;
-- 特点:会携带数据,但是不会携带约束条件
create table newemp select * from testdql.employees;
desc newemp;
select * from newemp;
drop table newemp;

-- 案例:复制testdql.employees表中的last_name,department_id,salary字段到新表emp表,但不复制数据
create table emp select last_name , department_id , salary from testdql.employees where 1=2;
desc emp;
select * from emp;

###########课堂练习##########
/*
	#设计一张员工信息表,要求如下
	1.编号(整型)
	2.员工工号(长度不超过10位)
	3.员工姓名(长度不超过255位)
	4.性别(存储男/女)
	5.年龄(不能存储负数)
	6.身份证号(18位,身份证中可能会出现X字符)
	7.入职时间(年-月-日)
	8.家庭地址(json数据存储省市区详细地址)
*/
drop table if EXISTS employee;
create table if not EXISTS employee(
	id int(11),
	empId varchar(10),
	name varchar(255),
	sex char(1),
	age TINYINT UNSIGNED,
	personId varchar(18),
	hiredate date,
	address json DEFAULT null
);
desc employee;


###########作业##########
-- 1. 创建表 dept1
-- name Null? type
-- id int(7)
-- name varchar(25)
drop table if EXISTS dept1;
create table if not EXISTS dept1(
	id int(7),
	name varchar(25)
);

-- 2. 将表 departments 中的数据插入新表 dept2 中
create table dept2 select * from testdql.departments;

-- 3. 创建表 emp5
-- name Null? type
-- id int(7)
-- First_name Varchar (25)
-- Last_name Varchar(25)
-- Dept_id int(7)
drop table if EXISTS emp5;
create table if not EXISTS emp5(
	id int(7),
	First_name varchar(25),
	Last_name varchar(25),
	Dept_id int(7)
);

-- 4. 将列 Last_name 的长度增加到 50
desc emp5;
alter table emp5 modify Last_name Varchar(50);
alter table emp5 change Last_name Last_name Varchar(55);

-- 5. 根据表 employees 创建 employees2
create table employees2 like testdql.employees;

-- 6. 删除表 emp5
drop table if EXISTS emp5;

-- 7. 将表 employees2 重命名为 emp5 
alter table employees2 rename to emp5;

-- 8 在表 dept1 和 emp5 中添加新列 test_column,并检查所作的操作
alter table dept1 add test_column varchar(50);
alter table emp5 add test_column varchar(50);
desc dept1;
desc emp5;

-- 9.直接删除表 emp5 中的列 department_id
alter table emp5 drop department_id;

#######完整性约束#######
/*
	-- 1.非空约束:not null
	用于限制该字段为必填项

	-- 2.默认约束:default
	严格来说,并不算约束。用于限制该字段没有显示插入值时,则直接使用默认值。
    
	-- 3.主键约束:primary key
	用于限制该字段值不能重复,设置为主键的字段默认不能为空。
	一个表只能有一个主键,也可以是组合主键。

	-- 4.唯一约束:unique
	用于限制该字段值不能重复。

	主键和唯一键的区别:其中主键字段不能为空,一个表中只能有一个主键;唯一键字段可以为空,一个表中可以有多个唯一键。
    
	-- 5.检查约束:check
	用于限制该字段值必须满足指定条件。
	check (age between 1 and 100),检查此时操作的年龄必须在1~100之间。

	-- 6.外键约束:foreign key
	用于限制两个表的关系,要求外键的字段值必须来自主表的关联列。
	要求
	1.主表的关联列和从表的关联列的数据类型一致,含义一样,命名无要求
	2.主表的关联列必须为主键
*/

-- 案例1
/*
	建表stu学生表:
	字段id为整型,且是主键;
	字段name为字符型长度为20,且是唯一不为空;
	字段gender为字符型长度为1,且默认为男;
	字段email为字符型长度为20,且不为空;
	字段age为整型,且是检查约束(0-100);
	字段majorid整型,外键关联major表中的id主键。

	建表major专业表:
	字段id为整型,且是主键;
	字段name为字符型长度为20,且是不为空。
*/

########一.列级约束##########
/*
	列级约束 
	1.在创建表,声明字段时,可直接在其后面声明约束条件   字段名  数据类型  约束条件
	2.支持约束:主键、非空、唯一、默认、检查
	3.不支持约束:外键
	4.不能自定义约束名字
*/
drop table if EXISTS major;
create table if not EXISTS major(
	id int primary key,       -- 主键约束
	name varchar(20) not null -- 非空约束
);

drop table if EXISTS stu;
create table if not EXISTS stu(
	id int primary key ,              		-- 主键约束 
	name varchar(20) not null unique, 		-- 唯一且非空约束
	gender char(1) DEFAULT '男',      		-- 默认约束
	email varchar(20) not null,       		-- 非空约束
	age int check(age between 0 and 100), -- 检查约束
	majorid int -- foreign key major(id)			-- 行级约束,不支持外键
);

-- 查看表结果
desc stu;
-- 测试,插入数据
insert into stu values(1 , 'aaa' , DEFAULT , '[email protected]' , 20 , 10);
-- 测试,主键约束:非空且唯一  Duplicate entry '1' for key 'stu.PRIMARY'   Column 'id' cannot be null
insert into stu values(null , 'aaa' , DEFAULT , '[email protected]' , 20 , 10);
-- 测试,唯一约束:字段值不可以重复 Duplicate entry 'aaa' for key 'stu.name'
insert into stu values(2 , 'aaa' , DEFAULT , '[email protected]' , 20 , 10);
-- 测试,非空约束:字段值不可以为null  Field 'email' doesn't have a default value
insert into stu(id , name , age , majorid) values(3 , 'bbb' , 20 , 10);
-- 测试,检查约束:字段值必须在检查的范围内  Check constraint 'stu_chk_1' is violated.
insert into stu values(3 , 'bbb' , DEFAULT , '[email protected]' , 120 , 10);


select * from stu;

########二.表级约束##########
/*
	表级约束
	1.在申明表时,在所有字段申明完结后,再添加约束条件  constraint 约束名 约束条件(字段) [REFERENCES 表(字段)]
	2.支持约束:主键、唯一、检查、外键
	3.不支持的约束:非空、默认
	4.可以支持自定义约束名称
*/
drop table if EXISTS stu;
create table if not EXISTS stu(
	id int,            
	name varchar(20), 	
	gender char(1),      		
	email varchar(20),       		
	age int, 
	majorid int,
	
	-- 添加约束条件
	-- constraint 约束名 约束条件(字段) [REFERENCES 表(字段)]
	primary key(id),																	 -- 主键约束
	constraint uk_name unique(name), 	 						 		 -- 唯一约束
	constraint ck_gender check(gender in ('男','女')), -- 检查约束
	constraint fk_majorid foreign key(majorid) REFERENCES major(id)  -- 外键约束
);

-- 查看表结构
desc stu;
-- 插入major表中数据
insert into major values(10 , '计算机');
insert into major values(20 , '数学');
insert into major values(30 , '英语');
-- 测试,外键约束
insert into stu values(1 , 'aaa' , '男' , '[email protected]' , 18 , 20 );
-- Cannot add or update a child row: a foreign key constraint fails (`testddl`.`stu`, CONSTRAINT `fk_majorid` FOREIGN KEY (`majorid`) REFERENCES `major` (`id`))
insert into stu values(2 , 'bbb' , '女' , '[email protected]' , 18 , 50 );
-- 测试,检查约束 Check constraint 'ck_gender' is violated.
insert into stu values(2 , 'bbb' , '非' , '[email protected]' , 18 , 30 );
-- 测试,主键约束 Duplicate entry '1' for key 'stu.PRIMARY'
insert into stu values(1 , 'bbb' , '女' , '[email protected]' , 18 , 30 );
-- 测试,唯一约束 1062 - Duplicate entry 'aaa' for key 'stu.uk_name'
insert into stu values(2 , 'aaa' , '女' , '[email protected]' , 18 , 30 );


####################常规写法####################
drop table if EXISTS stu;
create table if not EXISTS stu(
	id int primary key,   						 -- 主键约束            
	name varchar(20) not null unique,  -- 非空且唯一 	
	gender char(1) DEFAULT '男',       -- 默认约束
	email varchar(20) not null,        -- 非空约束      		
	age int check(age between 0 and 100), -- 检查约束 
	majorid int,
	
	-- 添加约束条件  
	-- constraint 外键名  foreign key(字段) REFERENCES 表(字段名)  携带外键名称
	-- constraint fk_majorid foreign key(majorid) REFERENCES major(id)  -- 外键约束
	
	-- foreign key(字段) REFERENCES 表(字段名)  不携带外键名称
	foreign key(majorid) REFERENCES major(id)  -- 外键约束
);

desc stu;


#########三.alter命令 添加 | 删除 约束#########
drop table if EXISTS stu;
create table if not EXISTS stu(
	id int,
	name varchar(20),
	gender char(1),
	email varchar(20),
	age int,
	majorid int
);
desc stu;

-- 3.1 alter 添加 | 删除  主键约束
-- 列级约束
alter table stu modify id int primary key;
-- 表级约束
alter table stu add primary key(id);
-- 删除主键约束
alter table stu drop primary key;

-- 3.2 alter 添加 | 删除  外键约束
-- 表级约束 携带名字
alter table stu add constraint fk_major_id foreign key(majorid) REFERENCES major(id);
-- 删除外键约束
alter table stu drop foreign key fk_major_id;

-- 表级约束 不携带名字
alter table stu add foreign key(majorid) REFERENCES major(id);
-- 删除外键约束
alter table stu drop FOREIGN KEY stu_ibfk_1;

-- 3.3 alter 添加 | 删除  唯一约束
-- 列级约束
alter table stu modify name varchar(20) unique; 
-- 表级约束
alter table stu add UNIQUE(name);
-- 删除唯一约束
alter table stu drop index name;

-- 3.4 alter 添加 | 删除  非空约束
-- 列级约束
alter table stu modify email varchar(20) not null;
-- 删除非空约束
alter table stu modify email varchar(20);

-- 3.5 alter 添加 | 删除  默认约束
-- 列级约束
alter table stu modify gender char(1) DEFAULT '男';
-- 删除默认约束
alter table stu modify gender char(1);

-- 3.6 alter 添加 | 删除  检查约束
-- 列级约束
alter table stu modify gender char(1) check(gender in('男','女'));
-- 表级约束
alter table stu add constraint ck_age check(age between 0 and 100);
-- 删除检查约束
alter table stu drop check ck_age;

insert into stu(id , gender) values(1 , '男');
insert into stu(id , gender) values(2 , '非');
insert into stu(id , age) values(4, 80);
insert into stu(id , age) values(5 , 120);
select * from stu;
desc stu;

/*
	总结 - alter添加表的约束
	1.默认约束 、 非空约束
	-- 列级约束
	alter table 表名 modify 字段 数据类型 [DEFAULT 默认值] | [NOT NULL];
	-- 删除约束
	alter table 表名 modify 字段 数据类型;
	
	2.外键约束、唯一约束、检查约束
	-- 表级约束
	alter table 表名 add constraint 约束名 [FOREIGN KEY  | UNIQUE | CHECK](字段名) [REFERENCES 表(字段)];
	-- 删除约束
	alter table 表名 drop [FOREIGN KEY  | index | CHECK] 约束名;
	
	3.主键约束
	-- 表级约束
	alter table 表名 add PRIMARY KEY(字段名);
	-- 删除约束
	alter table 表名 drop PRIMARY KEY;
*/

-- 面试题:主键约束和唯一约束的区别
/*
	面试题:主键约束和唯一约束的区别
			   
				      关键字      意义       出现次数    支持联合
	主键约束 primary key  非空且唯一    一次        支持
	唯一约束   unique       唯一        多次        支持
*/
drop table if EXISTS stu;
create table if not EXISTS stu(
	id int,
	name varchar(20) unique,
	gender char(1),
	email varchar(20) unique
);
-- Multiple primary key defined 不允许设置多个字段为主键,但是可以设置联合主键
alter table stu add primary key(id,name);
-- 测试联合主键  只有当id和name都一样时才认定是相同数据
insert into stu(id , name) values(1 , 'aaa');
insert into stu(id , name) values(1 , 'bbb');
insert into stu(id , name) values(2 , 'aaa');
-- 1062 - Duplicate entry '1-aaa' for key 'stu.PRIMARY'
insert into stu(id , name) values(1 , 'aaa');

-- 测试唯一键  不允许重复,可以出现多次,但是允许为null
insert into stu(name) values('ccc');
insert into stu(email) values('[email protected]');
insert into stu(email) values(null);
-- 1062 - Duplicate entry 'ccc' for key 'stu.name'
insert into stu(name) values('ccc');
-- 1062 - Duplicate entry '[email protected]' for key 'stu.email'
insert into stu(email) values('[email protected]');
select * from stu;
desc stu;


-- 面试题:三大范式
/*
	第一范式:表中的字段不可再分割 
	第二范式:当表与表之间有多对多的关系时,会建议创建关系表存储
	第三范式:当表与表之间有一对多的关系时,会建议主表中设置外键关联从表的主键
*/

#########案例#########
-- 案例2
-- 创建表qqinfo,里面包含qqid,添加主键约束、昵称nickname,添加唯一约束、邮箱email(添加非空约束)、性别gender
-- 删除表qqinfo
drop table if EXISTS qqinfo;
create table if not EXISTS qqinfo(
	qqid int primary key,
	nickname varchar(30) unique,
	email varchar(25) not null,
	gender char(1)
);

#########作业#########
-- 1. 向表 emp5 的 employee_id 列中添加 PRIMARY KEY 约束(my_emp_id_pk)
alter table emp5 drop PRIMARY KEY;
alter table emp5 add PRIMARY key(employee_id);
desc emp5;
-- 2. 向表 dept2 的 department_id 列中添加 PRIMARY KEY 约束(my_dept_id_pk)
alter table dept2 add PRIMARY key(department_id);
desc dept2;
-- 3. 向表 emp5 中添加列dept_id ,并在其中定义 FOREIGN KEY 约束,与之相关联的列是dept2 表中的department_id列。
alter table emp5 add dept_id int;
alter table emp5 add CONSTRAINT fk_dept_id FOREIGN KEY(dept_id) REFERENCES dept2(department_id);


2.3DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等 create / drop / alter/desc

########DML数据操作语言########
drop table if EXISTS stu;
create table if not EXISTS stu(
	id int primary key auto_increment,
	name varchar(20),
	-- age int , -- UNIQUE auto_increment
	gender char(1)
);

#一.插入数据
-- 1.1 插入全部字段
-- 语法:insert into 表名 values(值1,值2,....);
insert into stu values(1  , '张三' , '男');
insert into stu values(2  , '李四' , '女');
insert into stu values(3  , '王二麻' , '男');
select * from stu;

-- 1.2 插入部分字段
-- 语法:insert into 表名(字段1,字段2,..) values(值1,值2,...);
insert into stu(id,name) values(4,'张龙');
insert into stu(id,name) values(5,'赵虎');

-- 1.3 自增长
-- 设置主键且自增长
alter table stu modify id int primary key auto_increment;
-- 查看自增长的相关数据
show VARIABLES like '%auto_increment%';
-- auto_increment_increment 自增步长,默认1
-- auto_increment_offset    自增起始值,默认1
-- 修改自增长的起始值和步长
set auto_increment_offset = 10;
set auto_increment_increment = 10;

-- 1.4 批量插入的语法
insert into stu values(NULL,'aaa','男'),(NULL,'bbb','女'),(NULL,'ccc','女');
select * from stu;

/*
	关于自增长的情况
	1.查看自增长相关数据 show VARIABLES like '%auto_increment%';
	2.修改自增长的起始值和步长
	set auto_increment_offset = 10;
	set auto_increment_increment = 10;
	3.auto_increment是不是只能用在int类型的数据列上?
	1063 - Incorrect column specifier for column 'name'
	否,只要数据是数值类型,都可以使用auto_increment自增长,即int,double都可以使用;
	但是字符型例如varchar,不能够使用auto_increment自增长。
	4.auto_increment只能搭配primary key使用么?
	1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key
	否,搭配key键一起使用,例如:UNIQUE 、 FOREIGN KEY 、 PRIMARY KEY都可以。
	5.auto_increment能够在一张表中多次使用么?
	1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key
	否,auto_increment只能搭配表中的一个列字段使用。
*/

#二.更新数据
-- 语法:update 表名 set 字段1 = 值1 , 字段2 = 值2 ,... where 条件;
update stu set gender = '男';
update stu set gender = '女' where id = 20;
select * from stu;

#三.删除数据
-- 语法:delete from 表 where 条件;
delete from stu;
delete from stu where name = 'aaa';
select * from stu;

drop table stu;
desc stu;

truncate table stu;
desc stu;
insert into stu values(NULL,'aaa','男'),(NULL,'bbb','女'),(NULL,'ccc','女');
select * from stu;

/*	
	面试题 - delete 和 drop 和  truncate的区别
	delete
	1.delete from 表 where 条件
	2.DML操作 
	3.支持事务,允许事务回滚
	4.携带where条件
	5.返回受影响的行数 Affected rows:3
	6.删除数据后,再新增数据,自增长的值会接着继续的
	
	drop
	1.drop table 表
	2.DDL操作
	3.不支持事务
	4.不允许携带where条件
	5.不会返回受影响的行数
	6.drop表后,表结构被删除
	
	truncate
	1.truncate table 表
	2.不支持事务
	3.不允许携带where条件
	4.不会返回受影响的行数
	5.truncate表后,表结构不会删除
	6.删除数据后,再新增数据,自增长可重头开始
	7.truncate > delete 效率高些
*/


#########课堂作业#########
use testdql;
desc girl;
select * from girl;

-- 1、编写sql语句,修改表中编号是12女生的phone为'18209876579'
update girl set phone = '18209876579' where id = 12;

-- 2、编写sql语句,删除boyfriend_id为空的女生信息
delete from girl where boyfriend_id is null;
 
-- 3、编写sql语句,在girl表中插入一行数据
insert into girl values(null , '小小' , DEFAULT , '2000-02-05' , '12312312312' , null , 7);

-- 4、编写sql语句,实现查询girl表中的生日晚于1988年的女生信息
select * from girl where year(borndate)>1988;

#########作业#########
use testddl;

-- 1. 运行以下脚本创建表 my_employees
Create table my_employees(
	Id int(10),
	First_name varchar(10),
	Last_name varchar(10),
	Userid varchar(10),
	Salary double(10,2)
)
create table users(
	id int,
	userid varchar(10),
	department_id int
)

-- 2. 显示表 my_employees 的结构
desc my_employees;

-- 3. 向 my_employees 表中插入下列数据
-- ID FIRST_NAME LAST_NAME USERID SALARY
-- 1 patel Ralph Rpatel 895
-- 2 Dancs Betty Bdancs 860
-- 3 Biri Ben Bbiri 1100
-- 4 Newman Chad Cnewman 750
-- 5 Ropeburn Audrey Aropebur 1550
alter table my_employees modify id int(10) primary key auto_increment;
insert into my_employees values(null,'patel', 'Ralph' ,'Rpatel', 895),
(null,'Dancs', 'Betty' ,'Bdancs', 860),(null,'Biri', 'Ben' ,'Bbiri', 1100),
(null,'Newman', 'Chad' ,'Cnewman', 750),(null,'Ropeburn', 'Audrey' ,'Aropebur', 1550);
select * from my_employees;

-- 4. 向 users 表中插入数据
-- 1 Rpatel 10
-- 2 Bdancs 10
-- 3 Bbiri 20
-- 4 Cnewman 30
-- 5 Aropebur 40
alter table users modify id int primary key auto_increment;
insert into users values(null,'Rpatel',10),(null,'Bdancs',10),
(null , 'Bbiri' , 20),(null , 'Cnewman' , 30),(null , 'Aropebur' , 40);
select * from users;

-- 5. 将 3 号员工的 last_name 修改为“drelxer”
update my_employees set last_name = 'drelxer' where id = 3;

-- 6. 将所有工资少于 900 的员工的工资修改为 1000
update my_employees set salary = 1000 where salary<900;

-- 7. 将 userid 为 Bbiri 的 users 表和 my_employees 表的记录全部删除
delete from my_employees where userid='Bbiri';
delete from users where userid='Bbiri';

-- 8. 删除users所有数据
delete from users;

-- 9. 检查所作的修正
-- 10. 清空表my_employees
truncate table my_employees;

select * from my_employees;
select * from users;

2.4DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别

########DCL数据控制语言########
#DCL全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。

########1.用户管理########
-- 1)、查询用户
use mysql;
select * from user;

-- 2)、创建用户
-- 语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

-- 案例1:创建用户 newuser,只能够在当前主机 localhost访问,密码123456;
create user 'newuser'@'localhost' IDENTIFIED by '123456';

-- 案例2:创建用户 igeek ,可以在任意主机访问该数据库,密码123456 ;
create user 'igeek'@'%' IDENTIFIED by '123456';

-- 3)、修改用户密码
-- 语法:ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';

-- 案例:修改用户igeek的访问密码为1234;
alter user 'igeek'@'%' IDENTIFIED with mysql_native_password by '1234';

-- 4)、删除用户
-- DROP USER '用户名'@'主机名';

-- 案例:删除newuser@localhost用户
drop user 'newuser'@'localhost';
drop user 'igeek'@'%';

/*
	注意:
	1.主机名可以使用%通配。
	2.这类SQL开发人员操作的比较少,主要是DBA ( Database Administrator数据库管理员)使用。
*/


########2.权限控制########
-- 1)、查询权限
-- 语法:SHOW GRANTS FOR '用户名'@'主机名';

-- 案例:查询igeek用户权限
show grants for 'igeek'@'%';

-- 2)、授予权限
-- 语法:GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';

-- 案例:给igeek用户授予权限
grant all on testddl.* to 'igeek'@'%';
 
-- *.* 任意库任意表  all privileges所欲权限
grant all privileges on *.* to 'igeek'@'%' with grant option;

-- 3)、撤销权限
-- 语法:REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

-- 案例:撤销igeek用户权限
REVOKE all on testddl.* from 'igeek'@'%';
REVOKE all privileges , grant option from 'igeek'@'%';

-- 4)、刷新权限
flush privileges ;


/*
	注意:
	1.多个权限之间,使用逗号分隔
	2.授权时,数据库名和表名可以使用*进行通配,代表所有。
*/

2.5TCL(Transaction Control Language) :事务控制语言 事务提交commit,事务回滚rollback,保存点savepoint

##########TCL事务控制语言##########
/*
	一.事务
	TCL(Transaction Control Language ) 事务控制语言
	
	事务的概念:
	一个事务是由一条或者多条sql语句构成,这一条或者多条sql语句要么全部执行成功,要么全部执行失败!
默认情况下,每条单独的sql语句就是一个单独的事务!
*/
#InnoDB	DEFAULT	Supports transactions, row-level locking, and foreign keys	YES	YES	YES
#InnoDB 事务型存储引擎
show ENGINES;

/*
	二.事务的分类
	1.隐式事务:没有明显的开启和结束标记,比如DML语句的insert、update、delete语句本身就是一条事务。
	2.显示事务:具有明显的开启和结束标记,一般由多条sql语句组成,必须具有明显的开启和结束标记
	
	回滚rollback  提交commit  保存点savepoint
*/
-- 1.隐式事务
insert into major values(40 , '数学');
-- 此时回滚是无效的,因为隐式事务的情况下,默认一条SQL就是一个事务,insert执行完毕后事务也就结束了。
rollback;
select * from major;

-- 查看autocommit = ON 默认打开自动提交
show VARIABLES like '%autocommit%';

-- 取消事务自动开启
START TRANSACTION;
begin;
SET autocommit = 0;

-- 2.显示事务
drop table if EXISTS account;
create table if not EXISTS account(
	id int PRIMARY KEY auto_increment,
	name varchar(100),
	balance double(6,2)
);
insert into account values(null,"张三" , 1000.0);
insert into account values(null,"李四" , 1000.0);
select * from account;

/*
	2.1 假设执行过程中,出现异常,需要回滚
	1)、取消事务的自动提交
	2)、执行DML操作,模拟转账
	3)、回滚操作
	4)、手动commit提交事务,事务结束
*/
-- 1)、取消事务的自动提交
set autocommit = 0;
-- 2)、执行DML操作,模拟转账
update account set balance = balance - 500 where name = '张三';
update account set balance = balance + 500 where name = '李四';
-- 3)、回滚操作 Java catch捕获异常则执行回滚
rollback;
select * from account;
-- 4)、手动commit提交事务,事务结束
commit;

/*
	2.2 假设执行过程中,未出现异常,需要提交事务
	1)、取消事务的自动提交
	2)、执行DML操作,模拟转账
	3)、提交事务操作
*/
-- 1)、取消事务的自动提交
begin;
-- 2)、执行DML操作,模拟转账
update account set balance = balance - 500 where name = '张三';
update account set balance = balance + 500 where name = '李四';
-- 3)、提交事务操作  Java finally 
commit;
select * from account;

/*
	2.3 假设执行过程中,出现异常,需要回滚至指定的保存点
	1)、取消事务的自动提交
	2)、执行DML操作  设置保存点
	3)、回滚到指定保存点
	4)、提交事务操作
*/
-- 1)、取消事务的自动提交
set autocommit = 0;
-- 2)、执行DML操作  设置保存点
insert into account values(null , '王五' , 2000);
SAVEPOINT a;
update account set balance = balance - 500 where name = '李四';
SAVEPOINT b;
update account set balance = balance + 500 where name = '张三';
-- 3)、回滚到指定保存点
ROLLBACK to a;
-- 4)、提交事务
commit;
select * from account;


/*
	三.事务的特性
	1.原子性(Atomicity)
	指在事务中包含所有操作,要么都做,要么都不做(不可分割)
	2.一致性(Consistency)
	数据的改变保证一致
	3.隔离性(Isolation)
	数据库允许多个并发事务同时对其数据进行读写和修改的能力,防止多个事务并发执行时由于交叉执行而导致数据的不一致。
	4.持久性(Durability)
	事务处理结束之后,对数据的修改是永久的,即便你的系统出现故障,也不会丢失。(将数据存在了磁盘上)
	
	四.ACID的实现原理
	1.原子性:一个事务内的sql语句要么全部执行,要么全不执行,是通过undo log原理实现的。
	2.一致性: 
一致性是指事务执行之后,数据库的完整性约束没有被破坏,事务执行前后都是一个合法的数据状态。他的完整性主要体现在数据库主键要唯一,字段的类型、大小、长度、外键约束要符合要求。一致性是事务追求的最终目标,ACID中三种特性都是为了实现最终的一致性。 
	3.隔离性:写-写操作主要是通过锁实现的。如果是读- 写操作则是通过mvcc。 
	4.持久性:通过redo log保证的。
*/

2.6JDBC连接池

 2.6.1自定义连接池

package com.igeek.jdbc.d_pool.defineDataSource;

import com.igeek.jdbc.utils.JDBCUtils;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

/**
 * @Description 数据库连接池
 * @Author 李国勇
 * @Date 2022/12/8 11:39
 * 1.实现DataSource  接口
 * 2. 获得连接removeFirst 移除连接(归还连接)addLast
 */

public class MyDateSource implements DataSource {

    //1.数据库连接池
    private LinkedList pool;

    //2.向连接池加5个连接对象
    public MyDateSource(){
        pool=new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            pool.add(JDBCUtils.getConn());

        }
    }
    //3.获得连接 链表get没用
    @Override
    public Connection getConnection() throws SQLException {
        Connection connection = pool.removeFirst();
        return connection;
    }
    //4.归还连接
    public void close(Connection connection){
        pool.addLast(connection);

    }
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public  T unwrap(Class iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

2.6.2C3P0连接池

package com.igeek.jdbc.d_pool.c3p0;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description TODO
 * @Author 李国勇
 * @Date 2022/12/8 22:16
 */

public class C3P0Demo {
    
    public static void main(String[] args) {
        //1.创建new CombopooledDataSource数据源(数据连接和管理)
        ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
        Connection connection=null;
        try {
            //2.
           connection = dataSource.getConnection();
            System.out.println(connection);
           //3.
            PreparedStatement ppst =connection.prepareStatement("select * from girl where id=? ");
           ppst.setInt(1,13);
            //4.
            ResultSet rs = ppst.executeQuery();
            while(rs.next()){
                for (int i = 1; i < 7; i++) {
                    System.out.println(rs.getString(i));
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


	
  
  
      oracle.jdbc.driver.OracleDriver
      jdbc:oracle:thin:@127.0.0.1:1521:xe
      cm
      cm
      
      50
      
      50
      
      50
      
      10
  
 
  
   
     oracle.jdbc.driver.OracleDriver
     jdbc:oracle:thin:@127.0.0.1:1521:xe
     cm
     cm
  
  
  
  
     com.mysql.cj.jdbc.Driver
     jdbc:mysql://localhost:3306/testsql?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
     root
     123456
      
      500
      
      50
      
      100
      
      30
      
      3000
  

2.6.3Druid连接池

package com.igeek.jdbc.d_pool.druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @Description TODO
 * @Author 李国勇
 * @Date 2022/12/9 9:23
 */

public class DruidDemo {
    public static void main(String[] args) {
        InputStream is = DruidDemo.class.getResourceAsStream("druid.properties");
        Properties properties = new Properties();
        Connection connection = null;
        PreparedStatement ppst = null;
        try {
            //1.
            properties.load(is);

            //2.数据源(数据池)
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            System.out.println(dataSource);
            //3.获得连接对象
            connection = dataSource.getConnection();
            System.out.println(connection);
            //4.获得语句对象
            ppst = connection.prepareStatement("select name from girl where id =13");
            ResultSet rs = ppst.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString(1));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/testsql?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=123456
#\u521D\u59CB\u65F6\u6C60\u4E2D\u7684\u8FDE\u63A5-
initialSize=10
#\u6700\u5C0F\u6709\u591A\u5C11\u4E2A
minIdle=5
#\u6700\u5927\u6709\u591A\u5C11\u4E2A
maxActive=20
#\u8D85\u65F6\u7B49\u5F85\u65F6\u95F4  ms
maxWait=5000

##\u521D\u59CB\u65F6\u6C60\u4E2D\u7684\u8FDE\u63A5-
#initialSize=1
##\u6700\u5C0F\u6709\u591A\u5C11\u4E2A
#minIdle=1
##\u6700\u5927\u6709\u591A\u5C11\u4E2A
#maxActive=1
##\u8D85\u65F6\u7B49\u5F85\u65F6\u95F4  ms
#maxWait=3000

2.6.4ThredLocal+C3P0连接池+DbUtils 工具类(22-12-9)

package com.igeek.jdbc.dbutils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:41
 *
 * DbUtils 工具类
 * 一.QueryRunner类
 * 1.构造方法
 *      - 1.1 public QueryRunner() 无参构造方法,不需要提供数据源    用的多
 *      - 1.2 public QueryRunner(DataSource ds) 有参构造方法,必须提供数据源
 * 2.API方法
 *      - 2.1 查询
 *              public  T query(Connection conn, String sql, ResultSetHandler rsh, Object... params)  用的多
 *              若使用无参构造方法构建QueryRunner,此处连接对象必须提供
 *              public  T query(String sql, ResultSetHandler rsh, Object... params)
 *              若使用有参构造方法构建QueryRunner,连接对象将有提供的数据源进行创建
 *      - 2.2 增删改
 *              public int update(Connection conn, String sql, Object... params)   用的多
 *              若使用无参构造方法构建QueryRunner,此处连接对象必须提供
 *              public int update(String sql, Object... params)
 *              若使用有参构造方法构建QueryRunner,连接对象将有提供的数据源进行创建
 *
 * 二.ResultSetHandler 结果集处理器
 * 1.ScalarHandler  获取单值
 * 2.BeanHandler 获取单个对象
 * 3.BeanListHandler 获取多个对象
 * 4.MapListHandler 获取多表关联数据 Map  Key->String->字段名  Value->Object->表中字段对应的值
 */
public class BaseDao {

    private QueryRunner queryRunner = new QueryRunner();

    //查看单个值的方法
    public Object selectSingleValue(Connection conn , String sql , Object... params) throws SQLException {
        Object obj = queryRunner.query(conn, sql, new ScalarHandler(), params);
        return obj;
    }

    //查看单个对象的方法  T
    public T selectOne(Connection connection , String sql , Class clazz , Object... params) throws SQLException {
        T t = queryRunner.query(connection, sql, new BeanHandler<>(clazz), params);
        return t;
    }

    //查看多个对象的方法  List
    public List selectAll(Connection connection , String sql , Class clazz , Object... params) throws SQLException {
        List list = queryRunner.query(connection, sql, new BeanListHandler<>(clazz), params);
        return list;
    }

    //查看多表关联的数据  Employees 、 Department  List>
    public List> selectList(Connection connection , String sql , Object... params) throws SQLException {
        List> mapList = queryRunner.query(connection, sql, new MapListHandler(), params);
        return mapList;
    }

    //增删改
    public int update(Connection connection , String sql , Object... params) throws SQLException {
        int i = queryRunner.update(connection, sql, params);
        return i;
    }
}


package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Department;

import java.sql.SQLException;
import java.util.List;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class DepartmentDao extends BaseDao {

    //根据department_name查询部门列表
    public List selectAllDepartments(String query) throws SQLException {
        String sql = "select * from departments where department_name like concat('%',?,'%')";
        List departments = this.selectAll(JDBCUtils.getConn(), sql, Department.class, query);
        return departments;
    }

    //根据部门编号,更新部门信息
    public int updateDepartment(int deptId , String deptName) throws SQLException {
        String sql = "update departments set department_name = ? where department_id = ?";
        int i = this.update(JDBCUtils.getConn(), sql, deptName, deptId);
        return i;
    }

}


package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Employee;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class EmployeeDao extends BaseDao {

    //查询总记录数
    public Long selectCounts() throws SQLException {
        Long counts = (Long)this.selectSingleValue(JDBCUtils.getConn(), "select count(*) from employees");
        return counts;
    }

    //根据first_name查询员工列表
    public List selectAllEmployee(String query) throws SQLException {
        List employees = this.selectAll(JDBCUtils.getConn(),
                "select * from employees where first_name like concat(?,'%')",
                Employee.class, query);
        return employees;
    }

    //通过部门编号,查询员工姓名、薪资、部门编号及部门名称
    public List> selectEmpAndDeptByDeptId(int deptId) throws SQLException {
        String sql = "select e.first_name , e.salary , e.department_id , d.department_name \n" +
                "from employees e join departments d\n" +
                "on e.department_id = d.department_id\n" +
                "where e.department_id = ?";
        List> mapList = this.selectList(JDBCUtils.getConn(), sql, deptId);
        return mapList;
    }
}


package com.igeek.jdbc.dbutils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 *
 * ThreadLocal + C3P0
 */
public class JDBCUtils {

    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");

    //线程变量  Key->ThreadLocal  Value->Connection
    //多线程并发操作时,可以确保每个线程操作的是自己的线程变量中的值Connection连接对象,从而确保事务
    private static ThreadLocal tl = new ThreadLocal<>();

    //获取数据源的方法
    public static DataSource getDataSource(){
        return dataSource;
    }

    //获取连接的方法
    public static Connection getConn(){
        Connection connection = tl.get();
        try {
            if(connection==null || connection.isClosed()){
                connection = dataSource.getConnection();
                tl.set(connection);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //归还连接的方法
    public static void close(){
        Connection connection = tl.get();
        try {
            if(connection!=null && !connection.isClosed()){
                //C3P0的close(),归还连接至连接池中,不会进行物理释放
                connection.close();
                //为了确保不会发生内存泄漏
                tl.remove();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

2.6.5作业:学生管理系统 用JDBC做

需求描述:这是一个学生管理系统,以管理员身份登录,实现对学员以及年级信息的增删改
查操作

2.6.5.1.实体类

package com.igeek.jdbc.entity;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class Department {

    private int department_id;
    private String department_name;
    private int manager_id;
    private int location_id;

    public Department() {
    }

    public Department(int department_id, String department_name, int manager_id, int location_id) {
        this.department_id = department_id;
        this.department_name = department_name;
        this.manager_id = manager_id;
        this.location_id = location_id;
    }

    /**
     * 获取
     * @return department_id
     */
    public int getDepartment_id() {
        return department_id;
    }

    /**
     * 设置
     * @param department_id
     */
    public void setDepartment_id(int department_id) {
        this.department_id = department_id;
    }

    /**
     * 获取
     * @return department_name
     */
    public String getDepartment_name() {
        return department_name;
    }

    /**
     * 设置
     * @param department_name
     */
    public void setDepartment_name(String department_name) {
        this.department_name = department_name;
    }

    /**
     * 获取
     * @return manager_id
     */
    public int getManager_id() {
        return manager_id;
    }

    /**
     * 设置
     * @param manager_id
     */
    public void setManager_id(int manager_id) {
        this.manager_id = manager_id;
    }

    /**
     * 获取
     * @return location_id
     */
    public int getLocation_id() {
        return location_id;
    }

    /**
     * 设置
     * @param location_id
     */
    public void setLocation_id(int location_id) {
        this.location_id = location_id;
    }

    public String toString() {
        return "Department{department_id = " + department_id + ", department_name = " + department_name + ", manager_id = " + manager_id + ", location_id = " + location_id + "}";
    }
}


package com.igeek.jdbc.entity;

import java.util.Date;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class Employee {

    private int employee_id;
    private String first_name;
    private String last_name;
    private String email;
    private String phone_number;
    private String job_id;
    private double salary;
    private double commission_pct;
    private int manager_id;
    private int department_id;
    private Date hiredate;

    public Employee() {
    }

    public Employee(int employee_id, String first_name, String last_name, String email, String phone_number, String job_id, double salary, double commission_pct, int manager_id, int department_id, Date hiredate) {
        this.employee_id = employee_id;
        this.first_name = first_name;
        this.last_name = last_name;
        this.email = email;
        this.phone_number = phone_number;
        this.job_id = job_id;
        this.salary = salary;
        this.commission_pct = commission_pct;
        this.manager_id = manager_id;
        this.department_id = department_id;
        this.hiredate = hiredate;
    }

    /**
     * 获取
     * @return employee_id
     */
    public int getEmployee_id() {
        return employee_id;
    }

    /**
     * 设置
     * @param employee_id
     */
    public void setEmployee_id(int employee_id) {
        this.employee_id = employee_id;
    }

    /**
     * 获取
     * @return first_name
     */
    public String getFirst_name() {
        return first_name;
    }

    /**
     * 设置
     * @param first_name
     */
    public void setFirst_name(String first_name) {
        this.first_name = first_name;
    }

    /**
     * 获取
     * @return last_name
     */
    public String getLast_name() {
        return last_name;
    }

    /**
     * 设置
     * @param last_name
     */
    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }

    /**
     * 获取
     * @return email
     */
    public String getEmail() {
        return email;
    }

    /**
     * 设置
     * @param email
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * 获取
     * @return phone_number
     */
    public String getPhone_number() {
        return phone_number;
    }

    /**
     * 设置
     * @param phone_number
     */
    public void setPhone_number(String phone_number) {
        this.phone_number = phone_number;
    }

    /**
     * 获取
     * @return job_id
     */
    public String getJob_id() {
        return job_id;
    }

    /**
     * 设置
     * @param job_id
     */
    public void setJob_id(String job_id) {
        this.job_id = job_id;
    }

    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    /**
     * 获取
     * @return commission_pct
     */
    public double getCommission_pct() {
        return commission_pct;
    }

    /**
     * 设置
     * @param commission_pct
     */
    public void setCommission_pct(double commission_pct) {
        this.commission_pct = commission_pct;
    }

    /**
     * 获取
     * @return manager_id
     */
    public int getManager_id() {
        return manager_id;
    }

    /**
     * 设置
     * @param manager_id
     */
    public void setManager_id(int manager_id) {
        this.manager_id = manager_id;
    }

    /**
     * 获取
     * @return department_id
     */
    public int getDepartment_id() {
        return department_id;
    }

    /**
     * 设置
     * @param department_id
     */
    public void setDepartment_id(int department_id) {
        this.department_id = department_id;
    }

    /**
     * 获取
     * @return hiredate
     */
    public Date getHiredate() {
        return hiredate;
    }

    /**
     * 设置
     * @param hiredate
     */
    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }

    public String toString() {
        return "Employee{employee_id = " + employee_id + ", first_name = " + first_name + ", last_name = " + last_name + ", email = " + email + ", phone_number = " + phone_number + ", job_id = " + job_id + ", salary = " + salary + ", commission_pct = " + commission_pct + ", manager_id = " + manager_id + ", department_id = " + department_id + ", hiredate = " + hiredate + "}";
    }
}




2.6.5.2.Dao层设计

package com.igeek.jdbc.dbutils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:41
 *
 * DbUtils 工具类
 * 一.QueryRunner类
 * 1.构造方法
 *      - 1.1 public QueryRunner() 无参构造方法,不需要提供数据源    用的多
 *      - 1.2 public QueryRunner(DataSource ds) 有参构造方法,必须提供数据源
 * 2.API方法
 *      - 2.1 查询
 *              public  T query(Connection conn, String sql, ResultSetHandler rsh, Object... params)  用的多
 *              若使用无参构造方法构建QueryRunner,此处连接对象必须提供
 *              public  T query(String sql, ResultSetHandler rsh, Object... params)
 *              若使用有参构造方法构建QueryRunner,连接对象将有提供的数据源进行创建
 *      - 2.2 增删改
 *              public int update(Connection conn, String sql, Object... params)   用的多
 *              若使用无参构造方法构建QueryRunner,此处连接对象必须提供
 *              public int update(String sql, Object... params)
 *              若使用有参构造方法构建QueryRunner,连接对象将有提供的数据源进行创建
 *
 * 二.ResultSetHandler 结果集处理器
 * 1.ScalarHandler  获取单值
 * 2.BeanHandler 获取单个对象
 * 3.BeanListHandler 获取多个对象
 * 4.MapListHandler 获取多表关联数据 Map  Key->String->字段名  Value->Object->表中字段对应的值
 */
public class BaseDao {

    private QueryRunner queryRunner = new QueryRunner();

    //查看单个值的方法
    public Object selectSingleValue(Connection conn , String sql , Object... params) throws SQLException {
        Object obj = queryRunner.query(conn, sql, new ScalarHandler(), params);
        return obj;
    }

    //查看单个对象的方法  T
    public T selectOne(Connection connection , String sql , Class clazz , Object... params) throws SQLException {
        T t = queryRunner.query(connection, sql, new BeanHandler<>(clazz), params);
        return t;
    }

    //查看多个对象的方法  List
    public List selectAll(Connection connection , String sql , Class clazz , Object... params) throws SQLException {
        List list = queryRunner.query(connection, sql, new BeanListHandler<>(clazz), params);
        return list;
    }

    //查看多表关联的数据  Employees 、 Department  List>
    public List> selectList(Connection connection , String sql , Object... params) throws SQLException {
        List> mapList = queryRunner.query(connection, sql, new MapListHandler(), params);
        return mapList;
    }

    //增删改
    public int update(Connection connection , String sql , Object... params) throws SQLException {
        int i = queryRunner.update(connection, sql, params);
        return i;
    }
}


package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Department;

import java.sql.SQLException;
import java.util.List;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class DepartmentDao extends BaseDao {

    //根据department_name查询部门列表
    public List selectAllDepartments(String query) throws SQLException {
        String sql = "select * from departments where department_name like concat('%',?,'%')";
        List departments = this.selectAll(JDBCUtils.getConn(), sql, Department.class, query);
        return departments;
    }

    //根据部门编号,更新部门信息
    public int updateDepartment(int deptId , String deptName) throws SQLException {
        String sql = "update departments set department_name = ? where department_id = ?";
        int i = this.update(JDBCUtils.getConn(), sql, deptName, deptId);
        return i;
    }

}


package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Employee;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 */
public class EmployeeDao extends BaseDao {

    //查询总记录数
    public Long selectCounts() throws SQLException {
        Long counts = (Long)this.selectSingleValue(JDBCUtils.getConn(), "select count(*) from employees");
        return counts;
    }

    //根据first_name查询员工列表
    public List selectAllEmployee(String query) throws SQLException {
        List employees = this.selectAll(JDBCUtils.getConn(),
                "select * from employees where first_name like concat(?,'%')",
                Employee.class, query);
        return employees;
    }

    //通过部门编号,查询员工姓名、薪资、部门编号及部门名称
    public List> selectEmpAndDeptByDeptId(int deptId) throws SQLException {
        String sql = "select e.first_name , e.salary , e.department_id , d.department_name \n" +
                "from employees e join departments d\n" +
                "on e.department_id = d.department_id\n" +
                "where e.department_id = ?";
        List> mapList = this.selectList(JDBCUtils.getConn(), sql, deptId);
        return mapList;
    }
}


2.6.5.3.测试类

package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Department;
import org.junit.Test;

import java.sql.SQLException;
import java.util.List;

import static org.junit.Assert.*;

public class DepartmentDaoTest {

    DepartmentDao dao = new DepartmentDao();

    @Test
    public void selectAllDepartments() {
        List list = null;
        try {
            list = dao.selectAllDepartments("ac");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close();
        }
        System.out.println(list.size());
        System.out.println(list);
    }

    @Test
    public void updateDepartment() {
        int i = 0;
        try {
            i = dao.updateDepartment(10, "ABC");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close();
        }
        System.out.println(i>0?"更新成功":"更新失败");
    }
}

package com.igeek.jdbc.dbutils;

import com.igeek.jdbc.entity.Employee;
import org.junit.Test;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;

public class EmployeeDaoTest {

    EmployeeDao dao = new EmployeeDao();

    @Test
    public void selectCounts() {
        try {
            System.out.println("员工表中的所有记录数 = "+dao.selectCounts());
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close();
        }
    }

    @Test
    public void selectAllEmployee() {
        try {
            List employees = dao.selectAllEmployee("a");
            System.out.println(employees.size());
            System.out.println(employees);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close();
        }

    }

    @Test
    public void selectEmpAndDeptByDeptId() {
        try {
            List> mapList = dao.selectEmpAndDeptByDeptId(50);
            System.out.println(mapList.size());

            mapList.forEach(map->{
                Set keys = map.keySet();
                keys.forEach(key->{
                    Object value = map.get(key);
                    System.out.println("key = "+key+" , value = "+value);
                });
                System.out.println("=================");
            });
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close();
        }
    }
}

2.6.5.4.连接池C3P0+ThreadLocal

package com.igeek.jdbc.dbutils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Description TODO
 * @Author chenmin
 * @Version 1.0
 * @Date 2022/12/8 16:57
 *
 * ThreadLocal + C3P0
 */
public class JDBCUtils {

    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");

    //线程变量  Key->ThreadLocal  Value->Connection
    //多线程并发操作时,可以确保每个线程操作的是自己的线程变量中的值Connection连接对象,从而确保事务
    private static ThreadLocal tl = new ThreadLocal<>();

    //获取数据源的方法
    public static DataSource getDataSource(){
        return dataSource;
    }

    //获取连接的方法
    public static Connection getConn(){
        Connection connection = tl.get();
        try {
            if(connection==null || connection.isClosed()){
                connection = dataSource.getConnection();
                tl.set(connection);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //归还连接的方法
    public static void close(){
        Connection connection = tl.get();
        try {
            if(connection!=null && !connection.isClosed()){
                //C3P0的close(),归还连接至连接池中,不会进行物理释放
                connection.close();
                //为了确保不会发生内存泄漏
                tl.remove();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

你可能感兴趣的:(Java,java)