MYSQL學習教程總結

`students`#進階1: 基礎查詢
/*
語法:
select 查詢列表 from 表名;
類似於:System.out.println(打印東西);
特點:
1、查詢列表可以是:表中的字段、常量值、表達式、函數
2、查詢的結果是一個虛擬的表格
*/
USE `myemployees`;
#1查詢單個字段
SELECT last_name FROM employees;

#2.查詢表中的多個字段
SELECT last_name,salary,email FROM employees;

#3.查詢表中的所有字段
#方式一:
SELECT 
    `employee_id`,
    `first_name`,
    `last_name`,
    `phone_number`,
    `last_name`,
    `job_id`,
    `phone_number`,
    `job_id`,
    `salary`,
    `commission_pct`,
    `manager_id`,
    `department_id`,
    `hiredate` 
FROM
    employees ;
#方式二:  
 SELECT * FROM employees;
 
 #4.查詢常量值
 SELECT 100;
 SELECT 'john'; #字符串用' '來引用
 
 #5.查詢表達式
 SELECT 100%98;
  
 #6.查詢函數
 SELECT VERSION();
 
 #7.起別名
 /*
 ①便於理解
 ②如果要查詢的字段有重名的情況,使用別名可以區分開來
 */
 #方式一:使用as
SELECT 100%98 AS 結果;
SELECT last_name AS 姓,first_name AS 名 FROM employees;

#方式二:使用空格
SELECT last_name 姓,first_name 名 FROM employees;

#案例:查詢salary,顯示結果爲 out put
SELECT salary AS "out put" FROM employees;  #如果別名中有特殊字建議別名加上雙引號或者單引號

#8.去重
#案例:查詢員工表中涉及到的所有的部門編號
SELECT DISTINCT department_id FROM employees;

#9.+號的作用
/*
java中的+號:
①運算符,兩個操作數都爲數值型
②連接符,只要有一個操作數爲字符串
mysql中的+號:
僅僅只有一個功能:運算符
select 100+90; 兩個操作數都爲數值型,則做加法運算
select '123'+90;只要其中一方爲字符型,試圖將字符型數值轉換成數值型
			如果轉換成功,則繼續做加法運算
select 'john'+90;	如果轉換失敗,則將字符型數值轉換成0
select null+10; 只要其中一方爲null,則結果肯定爲null
*/
#案例:查詢員工名和姓連接成一個字段,並顯示爲 姓名
SELECT CONCAT('a','b','c');
SELECT CONCAT('a','b','c') AS 結果;
SELECT 
	CONCAT(last_name,first_name) AS 姓名
FROM
	employees;

/*
進階1基礎查詢
*/

#1.下面的語句是否可以執行成功  
SELECT last_name , job_id , salary AS sal
FROM employees; 

#2.下面的語句是否可以執行成功  
SELECT  *  FROM employees; 

#3.找出下面語句中的錯誤 
SELECT employee_id , last_name,
salary * 12 AS "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 
	IFNULL(commission_pct,0) AS 獎金率,
	commission_pct
FROM 
	employees;
#-------------------------------------------
#select null+10; 只要其中一方爲null,則結果肯定爲null
SELECT
	CONCAT(`first_name`,',',`last_name`,',',`job_id`,',',IFNULL(commission_pct,0)) AS out_put
FROM
	employees;
#-----------------------------------------------------------------------------------------------------------
#進階2:條件查詢
/*
語法:
	select 
		查詢列表   3
	from
		表名       1
	where
		篩選條件;  2        篩選 表名 的數據    執行順序   123  執行順序和書寫順序不一致
分類:
	一、按條件表達式篩選
	簡單條件運算符:> < = != <> >= <=
	二、按邏輯表達式篩選
	邏輯運算符:
	作用:用於連接條件表達式
		&& || !
		and or not
		
	&&和and:兩個條件都爲true,結果爲true,反之爲false
	||或or: 只要有一個條件爲true,結果爲true,反之爲false
	!或not: 如果連接的條件本身爲false,結果爲true,反之爲false
	三、模糊查詢
		like
		between and
		in
		is null
*/
#一、按條件表達式篩選
#案例1:查詢工資>12000的員工信息
SELECT 
	*
FROM
	employees
WHERE
	salary>12000;	
	
#案例2:查詢部門編號不等於90號的員工名和部門編號
SELECT 
	last_name,
	department_id
FROM
	employees
WHERE
	department_id<>90;
	
#二、按邏輯表達式篩選
#案例1:查詢工資z在10000到20000之間的員工名、工資以及獎金
SELECT
	last_name,
	salary,
	commission_pct
FROM
	employees
WHERE
	salary>=10000 AND salary<=20000;
#案例2:查詢部門編號不是在90到110之間,或者工資高於15000的員工信息
SELECT
	*
FROM
	employees
WHERE
	NOT(department_id>=90 AND  department_id<=110) OR salary>15000;
#三、模糊查詢
/*
like	
between and
in
is null|is not null
*/
#1.like
/*
特點:
①一般和通配符搭配使用
	通配符:
	% 任意多個字符,包含0個字符
	_ 任意單個字符
*、
#案例1:查詢員工名中包含字符a的員工信息
select 
	*
from
	employees
where
	last_name like '%a%';#abc
#案例2:查詢員工名中第三個字符爲n,第五個字符爲l的員工名和工資
select
	last_name,
	salary
FROM
	employees
WHERE
	last_name LIKE '__n_l%';

#案例3:查詢員工名中第二個字符爲_的員工名
SELECT
	last_name
FROM
	employees
WHERE
	last_name LIKE '_$_%' ESCAPE '$';(ESCAPE '$'轉義字符)# '_\_%'
#2.between and
/*
①使用between and 可以提高語句的簡潔度
②包含臨界值
③兩個臨界值不要調換順序

*/
#案例1:查詢員工編號在100到120之間的員工信息
SELECT
	*
FROM
	employees
WHERE
	employee_id >= 100 AND employee_id<=120;
#----------------------
SELECT
	*
FROM
	employees
WHERE
	employee_id BETWEEN 100 AND 120;
#3.in
/*
含義:判斷某字段的值是否屬於in列表中的某一項
特點:
	①使用in提高語句簡潔度
	②in列表的值類型必須一致或兼容
	③in列表中不支持通配符
*/
#案例:查詢員工的工種編號是 IT_PROG、AD_VP、AD_PRES中的一個員工名和工種編號
SELECT
	last_name,
	job_id
FROM
	employees
WHERE
	job_id = 'IT_PROT' OR job_id = 'AD_VP' OR JOB_ID ='AD_PRES';
#------------------
SELECT
	last_name,
	job_id
FROM
	employees
WHERE
	job_id IN( 'IT_PROT' ,'AD_VP','AD_PRES');

#4、is null
/*
=或<>不能用於判斷null值
is null或is not null 可以判斷null值
*/
#案例1:查詢沒有獎金的員工名和獎金率
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE
	commission_pct IS NULL;

#案例1:查詢有獎金的員工名和獎金率
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE
	commission_pct IS NOT NULL;

#----------以下爲×
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE 
	salary IS 12000;
		
#安全等於  <=>
#案例1:查詢沒有獎金的員工名和獎金率
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE
	commission_pct <=>NULL;
		
#案例2:查詢工資爲12000的員工信息
SELECT
	last_name,
	salary
FROM
	employees
WHERE 
	salary <=> 12000;	
	
#is null pk <=>
IS NULL:僅僅可以判斷NULL值,可讀性較高,建議使用
<=>    :既可以判斷NULL值,又可以判斷普通的數值,可讀性較低

# 案例講解
# 1 查詢員工號爲176的員工的姓名和部門號和年薪
SELECT CONCAT(`last_name`,`first_name`) AS 姓名,
      `salary`*12*(1+IFNULL(`commission_pct`,0)) AS 年薪
FROM
       `employees`;
# 2 查詢沒有獎金,且工資小於18000的salary,last_name
SELECT `salary`,`last_name` 
	FROM `employees`
WHERE `commission_pct` IS NULL AND `salary` < 18000;

# 3查詢`employees`表中,`job_id`不爲IT或者工資爲12000的員工信息
SELECT * 
	FROM `employees`
WHERE `job_id` <>'IT' OR `salary` = 12000;	
# 4 查看部門`departments`的表結構 
DESC `departments`;

# 5查詢部門`departments`表中涉及到了那些編號
SELECT DISTINCT `department_id` FROM `departments`;
#-------------------------------------------------------------------------------------
#進階三 排序查詢
/*
語法:
select 查詢列表              3
from 表名                    1
【where  篩選條件】          2
order by 排序的字段或表達式; 4        執行順序 1234

特點:
1、asc代表的是升序,可以省略
   desc代表的是降序
2、order by子句可以支持 單個字段、別名、表達式、函數、多個字段
3、order by子句在查詢語句的最後面,除了limit子句
*/

#1、按單個字段排序
SELECT * FROM employees ORDER BY salary DESC;

#2、添加篩選條件再排序
#案例:查詢部門編號>=90的員工信息,並按員工編號降序【添加篩選條件】
SELECT *
FROM employees
WHERE department_id>=90
ORDER BY employee_id DESC;

#3、按表達式排序
#案例:查詢員工信息 按年薪降序
SELECT *,salary*12*(1+IFNULL(commission_pct,0))
FROM employees
ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;

#4、按別名排序
#案例:查詢員工信息 按年薪升序
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY 年薪 ASC;

#5、按函數排序
#案例:查詢員工名,並且按名字的長度降序
SELECT LENGTH(last_name),last_name 
FROM employees
ORDER BY LENGTH(last_name) DESC;

#6、按多個字段排序
#案例:查詢員工信息,要求先按工資降序,再按employee_id升序【按多個字段進行排序】
SELECT *
FROM employees
ORDER BY salary DESC,employee_id ASC;

#排序查詢 案例講解
# 1 查詢員工的姓名和部門號和年薪,按年薪降序,按姓名升序
SELECT CONCAT(`last_name`,`first_name`) AS 姓名,`department_id`,`salary`*12*(1 + IFNULL(`commission_pct`,0)) AS 年薪
	FROM `employees`
ORDER BY 年薪 DESC ,姓名 ASC;	
# 2 選擇工資不在8000-17000的員工的姓名和工資,並按工資降序
SELECT CONCAT(`last_name`,`first_name`) AS 姓名,`salary` AS 工資
	FROM `employees`
WHERE `salary` NOT IN (salary BETWEEN 8000 AND 17000)
ORDER BY `salary` DESC;	
# 3 查詢郵箱中包含e的員工信息,並先按郵箱的字節數降序,在按部門號升序
SELECT * ,LENGTH(`email`)
	FROM`employees`
WHERE `email` LIKE '%e%'
ORDER BY LENGTH(`email`) DESC,`department_id` ASC;	

#---------------------------------------------------------------------------------

#進階4:常見函數
/*

概念:類似於java的方法,將一組邏輯語句封裝在方法體中,對外暴露方法名
好處:1、隱藏了實現細節  2、提高代碼的重用性
調用:select 函數名(實參列表) 【from 表】;
特點:
	①叫什麼(函數名)
	②幹什麼(函數功能)

分類:
	1、單行函數
	如 concat、length、ifnull等
	2、分組函數
	功能:做統計使用,又稱爲統計函數、聚合函數、組函數
	
常見函數:
	一、單行函數
	字符函數:
	length:獲取字節個數(utf-8一個漢字代表3個字節,gbk爲2個字節)
	concat
	substr
	instr
	trim
	upper
	lower
	lpad
	rpad
	replace
	
	數學函數:
	round
	ceil
	floor
	truncate
	mod
	
	日期函數:
	now
	curdate
	curtime
	year
	month
	monthname
	day
	hour
	minute
	second
	str_to_date
	date_format
	
	其他函數:
	version
	database
	user
	
	控制函數
	if
	case
*/

#一、字符函數
#1.length 獲取參數值的字節個數
SELECT LENGTH('john');
SELECT LENGTH('張三丰hahaha');
SHOW VARIABLES LIKE '%char%';

#2.concat 拼接字符串
`myemployees`

#3.upper、lower
SELECT UPPER('john');
SELECT LOWER('joHn');
#示例:將姓變大寫,名變小寫,然後拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name))  姓名 FROM employees;

#4.substr、substring
注意:Mysql中的索引從1開始
#截取從指定索引處後面所有字符
SELECT SUBSTR('李莫愁愛上了陸展元',7)  out_put;

#截取從指定索引處指定字符長度的字符
SELECT SUBSTR('李莫愁愛上了陸展元',1,3) out_put;

#案例:姓名中首字符大寫,其他字符小寫然後用_拼接,顯示出來
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2)))  out_put
FROM employees;

#5.instr 返回子串第一次出現的索引,如果找不到返回0
SELECT INSTR('楊不殷六俠悔愛上了殷六俠','殷八俠') AS out_put;

#6.trim去出前後空格
SELECT TRIM('    張翠山    ') AS out_put;
SELECT LENGTH(TRIM('    張翠山    ')) AS out_put;
#去除兩邊的aa兩個aa  也可以去除掉兩邊的a  'a'
SELECT TRIM('aa' FROM 'aaaaaaaaa張aaaaaaaaaaaa翠山aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')  AS out_put;

#7.lpad 用指定的字符實現左填充指定長度
SELECT LPAD('殷素素',10,'*') AS out_put;

#8.rpad 用指定的字符實現右填充指定長度
SELECT RPAD('殷素素',12,'ab') AS out_put;

#9.replace 替換 
SELECT REPLACE('周芷若周芷若周芷若周芷若張無忌愛上了周芷若','周芷若','趙敏') AS out_put;

#二、數學函數
#round 四捨五入
SELECT ROUND(-1.55);
SELECT ROUND(1.567,2);

#ceil 向上取整,返回>=該參數的最小整數
SELECT CEIL(-1.02);

#floor 向下取整,返回<=該參數的最大整數
SELECT FLOOR(-9.99);

#truncate 截斷
SELECT TRUNCATE(1.69999,1);

#mod取餘
/*
mod(a,b) :  a-a/b*b

mod(-10,-3):-10- (-10)/(-3)*(-3)=-1
*/
SELECT MOD(10,-3);
SELECT 10%3;

#三、日期函數

#now 返回當前系統日期+時間
SELECT NOW();

#curdate 返回當前系統日期,不包含時間
SELECT CURDATE();

#curtime 返回當前時間,不包含日期
SELECT CURTIME();

#可以獲取指定的部分,年、月、日、小時、分鐘、秒
SELECT YEAR(NOW()) 年;
SELECT YEAR('1998-1-1') 年;
SELECT  YEAR(hiredate) 年 FROM employees;
SELECT MONTH(NOW()) 月;
SELECT MONTHNAME(NOW()) 月;

#str_to_date 將字符通過指定的格式轉換成日期
SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS out_put;

#查詢入職日期爲1992--4-3的員工信息
SELECT * FROM employees WHERE hiredate ='1992-4-3';
SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');

#date_format 將日期轉換成字符
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put;

#查詢有獎金的員工名和入職日期(xx月/xx日 xx年)
SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入職日期
FROM employees
WHERE commission_pct IS NOT NULL;


#四、其他函數
SELECT VERSION();
SELECT DATABASE();
SELECT USER();

#五、流程控制函數
#1.if函數: if else 的效果

SELECT IF(10<5,'大','小');

SELECT last_name,commission_pct,IF(commission_pct IS NULL,'沒獎金,呵呵','有獎金,嘻嘻') 備註
FROM employees;

#2.case函數的使用一: switch case 的效果

/*
java中
switch(變量或表達式){
	case 常量1:語句1;break;
	...
	default:語句n;break;
}

mysql中

case 要判斷的字段或表達式
when 常量1 then 要顯示的值1或語句1;
when 常量2 then 要顯示的值2或語句2;
...
else 要顯示的值n或語句n;
end
*/

/*案例:查詢員工的工資,要求

部門號=30,顯示的工資爲1.1倍
部門號=40,顯示的工資爲1.2倍
部門號=50,顯示的工資爲1.3倍
其他部門,顯示的工資爲原工資

*/

SELECT salary 原始工資,department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工資
FROM employees;

#3.case 函數的使用二:類似於 多重if
/*
java中:
if(條件1){
	語句1;
}else if(條件2){
	語句2;
}
...
else{
	語句n;
}

mysql中:

case 
when 條件1 then 要顯示的值1或語句1
when 條件2 then 要顯示的值2或語句2
。。。
else 要顯示的值n或語句n
end
*/

#案例:查詢員工的工資的情況
如果工資>20000,顯示A級別
如果工資>15000,顯示B級別
如果工資>10000,顯示C級別
否則,顯示D級別
SELECT salary,
CASE 
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END AS 工資級別
FROM employees;

#案例講解  單行函數
# 1 顯示系統的時間(注:日期+時間)
SELECT NOW();

# 2 查詢員工號,姓名,工資以及工資提高百分之20%後的結果(new salary)
SELECT `employee_id`,CONCAT(`last_name`,`first_name`),`salary`,salary*(1 + 0.2) AS "new salary" 
FROM `employees`;

# 3 將員工的姓名按首字母排序,並寫出姓名的長度(Length)
SELECT SUBSTR(`last_name`,1,1),LENGTH(`last_name`)
FROM `employees`
ORDER BY SUBSTR(`last_name`,1,1);
# 4 做一個查詢產生下面的結果
<last_name> earns <salary> monthly but wants <salary*3>
Dream Salary
King earns 24000 monthly but wants 72000

SELECT CONCAT(`last_name`, "earns",`salary`,"monthly but wants",`salary`*3) AS DreamSalary
FROM `employees`;
WHERE `salary` = 24000;

#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 AS  job,
CASE job_id
WHEN 'AD_PRES' THEN 'A' 
WHEN 'ST_MAN' THEN 'B' 
WHEN 'IT_PROG' THEN 'C' 
WHEN 'SA_PRE' THEN 'D'
WHEN 'ST_CLERK' THEN 'E'
END AS Grade
FROM employees
WHERE job_id = 'AD_PRES';

#二、分組函數
/*
功能:用作統計使用,又稱爲聚合函數或統計函數或組函數
分類:
sum 求和、avg 平均值、max 最大值 、min 最小值 、count 計算個數
特點:
1、sum、avg一般用於處理數值型
   max、min、count可以處理任何類型
2、以上分組函數都忽略null值
3、可以和distinct搭配實現去重的運算
4、count函數的單獨介紹
一般使用count(*)用作統計行數
5、和分組函數一同查詢的字段要求是group by後的字段
*/

#1、簡單 的使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT COUNT(salary) FROM employees;
SELECT SUM(salary) 和,AVG(salary) 平均,MAX(salary) 最高,MIN(salary) 最低,COUNT(salary) 個數
FROM employees;

SELECT SUM(salary) 和,ROUND(AVG(salary),2) 平均,MAX(salary) 最高,MIN(salary) 最低,COUNT(salary) 個數
FROM employees;

#2、參數支持哪些類型
SELECT SUM(last_name) ,AVG(last_name) FROM employees;
SELECT SUM(hiredate) ,AVG(hiredate) FROM employees;
SELECT MAX(last_name),MIN(last_name) FROM employees;
SELECT MAX(hiredate),MIN(hiredate) FROM employees;
SELECT COUNT(commission_pct) FROM employees;
SELECT COUNT(last_name) FROM employees;
#3、是否忽略null
SELECT SUM(commission_pct) ,AVG(commission_pct),SUM(commission_pct)/35,SUM(commission_pct)/107 FROM employees;
SELECT MAX(commission_pct) ,MIN(commission_pct) FROM employees;
SELECT COUNT(commission_pct) FROM employees;
SELECT commission_pct FROM employees;
#4、和distinct搭配
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;
#5、count函數的詳細介紹
SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
效率:
MYISAM存儲引擎下  ,COUNT(*)的效率高
INNODB存儲引擎下,COUNT(*)和COUNT(1)的效率差不多,比COUNT(字段)要高一些
#6、和分組函數一同查詢的字段有限制
SELECT AVG(salary),employee_id  FROM employees;

#案例講解
# 1查詢公司員工工資的最大值,最小值,平均值,總和
SELECT MAX(`salary`) 最大值,MIN(`salary`) 最小值,AVG(`salary`) 平均值,SUM(`salary`) 總和
FROM `employees`;

# 2查詢員工表中的最大入職時間和最小入職時間的相差天數 (DIFFRENCE)
SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) DIFFRENCE
FROM employees;

# 3查詢部門編號爲90的員工個數
SELECT COUNT(1) FROM `employees`
WHERE `department_id` = 90;

#------------------------------------------------------------------------
#進階5:分組查詢
/*
語法:
select 分組函數 列(字段)(要求出現在group by的後面)
from 表
【where 篩選條件】
group by 分組的字段
【order by 排序的字段】;

特點:
1、和分組函數一同查詢的字段必須是group by後出現的字段
2、篩選分爲兩類:分組前篩選和分組後篩選
		針對的表			位置		連接的關鍵字
分組前篩選	原始表				group by前	where
	
分組後篩選	group by後的結果集    		group by後	having

####分組函數做條件肯定是放在having子句中

問題1:分組函數做篩選能不能放在where後面
答:不能

問題2:where——group by——having

一般來講,能用分組前篩選的,儘量使用分組前篩選,提高效率

3、group by可以按單個字段也可以按多個字段
4、group by可以搭配着排序使用
*/
#引入:查詢每個部門的員工個數

SELECT COUNT(*) FROM employees WHERE department_id=90;
#1.簡單的分組
#案例1:查詢每個工種的員工平均工資
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

#案例2:查詢每個位置的部門個數
SELECT COUNT(*),location_id
FROM departments
GROUP BY location_id;

#2、可以實現分組前的篩選

#案例1:查詢郵箱中包含a字符的 每個部門的最高工資
SELECT MAX(salary),department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;

#案例2:查詢有獎金的每個領導手下員工的平均工資
SELECT AVG(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

#3、分組後篩選
#案例:查詢哪個部門的員工個數>5
#①查詢每個部門的員工個數
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id;

#② 篩選剛纔①結果    刷選員工的個數
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id                #先進行分組  然後進行篩選
HAVING COUNT(*)>5;

#案例2:每個工種有獎金的員工的最高工資>12000的工種編號和最高工資
SELECT job_id,MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary)>12000;    ####分組函數做條件肯定是放在having子句中   

#案例3:領導編號>102的每個領導手下的最低工資大於5000的領導編號和最低工資

SELECT manager_id,MIN(salary)
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;

#按員工姓名的長度分組,查詢每一組的員工個數,篩選員工個數>5的有那些

#查詢每個長度的員工個數
SELECT COUNT(*),LENGTH(last_name) len_name
FROM `employees`
GROUP BY LENGTH(`last_name`);
#添加帥選條件
SELECT COUNT(*),LENGTH(last_name) len_name
FROM `employees`
GROUP BY LENGTH(`last_name`)
HAVING COUNT(*) > 5;

#4.添加排序
#案例:每個工種有獎金的員工的最高工資>6000的工種編號和最高工資,按最高工資升序
SELECT job_id,MAX(salary) m
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING m>6000
ORDER BY m ;

#5.按多個字段分組
#案例:查詢每個工種每個部門的最低工資,並按最低工資降序
SELECT MIN(salary),job_id,department_id
FROM employees
GROUP BY department_id,job_id
ORDER BY MIN(salary) DESC;

#案例講解
#1.查詢各job_id的員工工資的最大值,最小值,平均值,總和,並按job_id升序
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary),job_id
FROM employees
GROUP BY job_id
ORDER BY job_id;

#2.查詢員工最高工資和最低工資的差距(DIFFERENCE)
SELECT MAX(salary)-MIN(salary) DIFFRENCE
FROM employees;

#3.查詢各個管理者手下員工的最低工資,其中最低工資不能低於6000,沒有管理者的員工不計算在內
SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary)>=6000;

#4.查詢所有部門的編號,員工數量和工資平均值,並按平均工資降序
SELECT department_id,COUNT(*),AVG(salary) a
FROM employees
GROUP BY department_id
ORDER BY a DESC;

#5.選擇具有各個job_id的員工人數
SELECT COUNT(*) 個數,job_id
FROM employees
GROUP BY job_id;

#-------------------------------------------------------------------------------------
#進階6:連接查詢
/*
含義:又稱多表查詢,當查詢的字段來自於多個表時,就會用到連接查詢

笛卡爾乘積現象:表1 有m行,表2有n行,結果=m*n行

發生原因:沒有有效的連接條件
如何避免:添加有效的連接條件

分類:
	按年代分類:
	sql92標準:僅僅支持內連接
	sql99標準【推薦】:支持內連接+外連接(左外和右外)+交叉連接
	
	按功能分類:
		內連接:
			等值連接
			非等值連接
			自連接
		外連接:
			左外連接
			右外連接
			全外連接
		
		交叉連接
*/
SELECT * FROM beauty;

SELECT * FROM boys;

SELECT NAME,boyName FROM boys,beauty;  #笛卡爾乘積

SELECT NAME,boyName FROM boys,beauty
WHERE beauty.boyfriend_id= boys.id;

#一、sql92標準
#1、等值連接
/*
① 多表等值連接的結果爲多表的交集部分
②n表連接,至少需要n-1個連接條件
③ 多表的順序沒有要求
④一般需要爲表起別名
⑤可以搭配前面介紹的所有子句使用,比如排序、分組、篩選
*/

#案例1:查詢女神名和對應的男神名
SELECT NAME,boyName 
FROM boys,beauty
WHERE beauty.boyfriend_id= boys.id;

#案例2:查詢員工名和對應的部門名

SELECT last_name,department_name
FROM employees,departments
WHERE employees.`department_id`=departments.`department_id`;

#2、爲表起別名
/*
①提高語句的簡潔度
②區分多個重名的字段

注意:如果爲表起了別名,則查詢的字段就不能使用原來的表名去限定

*/
#2,查詢員工名、工種號、工種名
SELECT e.last_name,e.job_id,j.job_title
FROM employees  e,jobs j
WHERE e.`job_id`=j.`job_id`;

#3、兩個表的順序是否可以調換       可以調換

#查詢員工名、工種號、工種名

SELECT e.last_name,e.job_id,j.job_title
FROM jobs j,employees e
WHERE e.`job_id`=j.`job_id`;

#4、可以加篩選
#案例:查詢有獎金的員工名、部門名

SELECT last_name,department_name,commission_pct

FROM employees e,departments d
WHERE e.`department_id`=d.`department_id`
AND e.`commission_pct` IS NOT NULL;

#案例2:查詢城市名中第二個字符爲o的部門名和城市名

SELECT department_name,city
FROM departments d,locations l
WHERE d.`location_id` = l.`location_id`
AND city LIKE '_o%';

#5、可以加分組

#案例1:查詢每個城市的部門個數

SELECT COUNT(*) 個數,city
FROM departments d,locations l
WHERE d.`location_id`=l.`location_id`
GROUP BY city;

#案例2:查詢有獎金的每個部門的部門名和部門的領導編號和該部門的最低工資
SELECT department_name,d.`manager_id`,MIN(salary)
FROM departments d,employees e
WHERE d.`department_id`=e.`department_id`
AND commission_pct IS NOT NULL
GROUP BY department_name,d.`manager_id`;

#6、可以加排序
#案例:查詢每個工種的工種名和員工的個數,並且按員工個數降序
SELECT job_title,COUNT(*)
FROM employees e,jobs j
WHERE e.`job_id`=j.`job_id`
GROUP BY job_title
ORDER BY COUNT(*) DESC;

#7、可以實現三表連接?
#案例:查詢員工名、部門名和所在的城市
SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.`department_id`=d.`department_id`
AND d.`location_id`=l.`location_id`
AND city LIKE 's%'
ORDER BY department_name DESC;

#2、非等值連接
#案例1:查詢員工的工資和工資級別
SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.`lowest_sal` AND g.`highest_sal`
AND g.`grade_level`='A';

/*
select salary,employee_id from employees;
select * from job_grades;
CREATE TABLE job_grades
(grade_level VARCHAR(3),
 lowest_sal  int,
 highest_sal int);

INSERT INTO job_grades
VALUES ('A', 1000, 2999);

INSERT INTO job_grades
VALUES ('B', 3000, 5999);

INSERT INTO job_grades
VALUES('C', 6000, 9999);

INSERT INTO job_grades
VALUES('D', 10000, 14999);

INSERT INTO job_grades
VALUES('E', 15000, 24999);

INSERT INTO job_grades
VALUES('F', 25000, 40000);

*/
#3、自連接(自己連接自己)
#案例:查詢 員工名和上級的名稱
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name
FROM employees e,employees m
WHERE e.`manager_id`=m.`employee_id`;

#基礎測試
# 1顯示員工表的最大工資,平均工資
SELECT MAX(`salary`) 最大工資,AVG(`salary`) 平均工資
FROM `employees`;

# 2查詢員工表的`employee_id`,`job_id`,`last_name`並按`department_id`降序`salary`升序
SELECT `employee_id`,`job_id`,`last_name`
FROM `employees`
ORDER BY `department_id` DESC,`salary` ASC;

# 3查詢員工表的`job_id`中包含a 和 e的,並且a在e的前面
SELECT `job_id` FROM `employees`
WHERE `job_id` LIKE '%a%e%';

# 4已知表student,裏面有id(學號),name,gradeId(年級編號)
#    已知表grade,裏面有id(年紀編號),'name'(年級名)
 #   已知表result,裏面由id,score,studentNo(學號)
 #要求查詢姓名,年級名,成績
SELECT s.name,g.name,r.score
FROM student s,grade g,result r
WHERE s.id = r.studentNo
AND g.id = s.gradeid 
 
# 5 顯示當前日期,以及去前後空格,截取字符串的函數
SELECT NOW();
SELECT TRIM(字符 FROM '')
SELECT SUBSTR(str,startIndex);

#作業講解
#1.顯示所有員工的姓名,部門號和部門名稱。
SELECT `last_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 `job_id`,`location_id`
FROM `employees` e,`departments` d
WHERE e.`department_id` = d.`department_id`
AND e.`department_id` = 90;
#3.選擇所有有獎金的員工的
last_name , department_name , location_id , city
SELECT last_name , department_name , l.location_id , city
FROM `departments` d,`employees` e,`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 last_name , job_id , d.department_id , department_name 
FROM `employees` e,`departments` d,`locations` l
WHERE e.`department_id` = d.`department_id`
AND d.`location_id` = l.`location_id`
AND `city` = 'Toronto';

#5.查詢每個工種、每個部門的部門名、工種名和最低工資
SELECT `department_name` ,`job_title`,MIN(salary)
FROM `employees` e,`jobs` j,`departments` d
WHERE e.`job_id` = j.`job_id`
AND e.`department_id` = d.`department_id`
GROUP BY `job_title`,`department_name`;

#6.查詢每個國家下的部門個數大於2的國家編號
SELECT `country_id`, COUNT(*) 部門個數
FROM `locations` l,`departments` d
WHERE l.`location_id` = d.`location_id`
GROUP BY `country_id`
HAVING COUNT(*) >2;

#7、選擇指定員工的姓名,員工號,以及他的管理者的姓名和員工號,結果類似於下面的格式
employees	Emp#	manager	Mgr#
kochhar		101	king	100

SELECT e.last_name employees,e.employee_id "Emp#",m.last_name manager,m.employee_id "Mgr#"
FROM employees e,employees m
WHERE e.manager_id = m.employee_id
AND e.last_name='kochhar';
#----------------------------------------------------------------------------------------------
#二、連接查詢   sql99語法
/*
語法:
	select 查詢列表
	from 表1 別名 【連接類型】
	join 表2 別名 
	on 連接條件
	【where 篩選條件】
	【group by 分組】
	【having 篩選條件】
	【order by 排序列表】
【連接類型】分類:
內連接(★):inner         相當於等值連接
外連接
	左外(★):left 【outer】
	右外(★):right 【outer】
	全外:full【outer】
交叉連接:cross 

*/


#一)內連接
/*
語法:

select 查詢列表
from 表1 別名
inner join 表2 別名
on 連接條件;

分類:
等值
非等值
自連接

特點:
①添加排序、分組、篩選
②inner可以省略
③ 篩選條件放在where後面,連接條件放在on後面,提高分離性,便於閱讀
④inner join連接和sql92語法中的等值連接效果是一樣的,都是查詢多表的交集
*/

#1、等值連接
#案例1.查詢員工名、部門名
SELECT last_name,department_name
FROM departments d
INNER JOIN  employees e
ON e.`department_id` = d.`department_id`;

SELECT last_name,department_name
FROM departments d
JOIN  employees e
ON e.`department_id` = d.`department_id`;

#案例2.查詢名字中包含e的員工名和工種名(添加篩選)
SELECT last_name,job_title
FROM employees e
INNER JOIN jobs j
ON e.`job_id`=  j.`job_id`
WHERE e.`last_name` LIKE '%e%';

#3. 查詢部門個數>3的城市名和部門個數,(添加分組+篩選)

#①查詢每個城市的部門個數
#②在①結果上篩選滿足條件的
SELECT city,COUNT(*) 部門個數
FROM departments d
INNER JOIN locations l
ON d.`location_id`=l.`location_id`
GROUP BY city
HAVING COUNT(*)>3;

#案例4.查詢哪個部門的員工個數>3的部門名和員工個數,並按個數降序(添加排序)

#①查詢每個部門名的員工個數
SELECT COUNT(*),department_name
FROM employees e
INNER JOIN departments d
ON e.`department_id`=d.`department_id`
GROUP BY department_name

#② 在①結果上篩選員工個數>3的記錄,並排序

SELECT COUNT(*) 個數,department_name
FROM employees e
INNER JOIN departments d
ON e.`department_id`=d.`department_id`
GROUP BY department_name
HAVING COUNT(*)>3
ORDER BY COUNT(*) DESC;

#5.查詢員工名、部門名、工種名,並按部門名降序(添加三表連接)
SELECT last_name,department_name,job_title
FROM employees e
INNER JOIN departments d ON e.`department_id`=d.`department_id`
INNER JOIN jobs j ON e.`job_id` = j.`job_id`
ORDER BY department_name DESC;

#二)非等值連接

#查詢員工的工資級別

SELECT salary,grade_level
FROM employees e
 JOIN job_grades g
 ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`;
 
 
 #查詢工資級別的個數>20的個數,並且按工資級別降序
SELECT COUNT(*),grade_level
FROM employees e
JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`
GROUP BY grade_level
HAVING COUNT(*)>20
ORDER BY grade_level DESC;
 
 
 #三)自連接
 
 #查詢員工的名字、上級的名字
 SELECT e.last_name,m.last_name
 FROM employees e
 JOIN employees m
 ON e.`manager_id`= m.`employee_id`;
 
  #查詢姓名中包含字符k的員工的名字、上級的名字
 SELECT e.last_name,m.last_name
 FROM employees e
 JOIN employees m
 ON e.`manager_id`= m.`employee_id`
 WHERE e.`last_name` LIKE '%k%';
 
 #二、外連接
 /*
 應用場景:用於查詢一個表中有,另一個表沒有的記錄
 
 特點:
 1、外連接的查詢結果爲主表中的所有記錄
	如果從表中有和它匹配的,則顯示匹配的值
	如果從表中沒有和它匹配的,則顯示null
	外連接查詢結果=內連接結果+主表中有而從表沒有的記錄
 2、左外連接,left join左邊的是主表
    右外連接,right join右邊的是主表
 3、左外和右外交換兩個表的順序,可以實現同樣的效果 
 4、全外連接=內連接的結果+表1中有但表2沒有的+表2中有但表1沒有的
 */
 #引入  查詢男朋友 不在男神表的的女神名
 SELECT * FROM beauty;
 SELECT * FROM boys;
 
 #左外連接
 SELECT b.name,bo.*
 FROM beauty b
 LEFT OUTER JOIN boys bo
 ON b.`boyfriend_id` = bo.`id`;
 WHERE bo.id IS NULL;
  #右外連接                 左外右外效果一樣
 SELECT b.name,bo.*
 FROM boys bo
 RIGHT OUTER JOIN beauty b
 ON b.`boyfriend_id` = bo.`id`;
 WHERE bo.id IS NULL;
 
 #左外連接
 SELECT b.*,bo.*
 FROM boys bo
 LEFT OUTER JOIN beauty b
 ON b.`boyfriend_id` = bo.`id`
 
 
 WHERE b.`id` IS NULL;
 
 #案例1:查詢哪個部門沒有員工
 #左外
 SELECT d.*,e.employee_id
 FROM departments d
 LEFT OUTER JOIN employees e
 ON d.`department_id` = e.`department_id`
 
 WHERE e.`employee_id` IS NULL;
  
 #右外
 SELECT d.*,e.employee_id
 FROM employees e
 RIGHT OUTER JOIN departments d
 ON d.`department_id` = e.`department_id`
 WHERE e.`employee_id` IS NULL;
 
 #全外
 USE girls;
 SELECT b.*,bo.*
 FROM beauty b
 FULL OUTER JOIN boys bo
 ON b.`boyfriend_id` = bo.id;

 #交叉連接       笛卡爾乘積
 SELECT b.*,bo.*
 FROM beauty b
 CROSS JOIN boys bo;
 
 #sql92和 sql99pk
 /*
 功能:sql99支持的較多
 可讀性:sql99實現連接條件和篩選條件的分離,可讀性較高
 */
 
 #案例講解
 #一、查詢編號>3的女神的男朋友信息,如果有則列出詳細,如果沒有,用null填充
SELECT b.id,b.name,bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE b.`id`>3;
#二、查詢哪個城市沒有部門
SELECT city,d.*
FROM departments d
RIGHT OUTER JOIN locations l 
ON d.`location_id`=l.`location_id`
WHERE  d.`department_id` IS NULL;

#三、查詢部門名爲SAL或IT的員工信息       那個主要當主表
SELECT e.*,d.department_name,d.`department_id`
FROM departments  d
LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
WHERE d.`department_name` IN('SAL','IT');


SELECT * FROM departments
WHERE `department_name` IN('SAL','IT');

#------------------------------------------------------------------------------------
#進階7:子查詢
/*
含義:
出現在其他語句中的select語句,稱爲子查詢或內查詢
外部的查詢語句,稱爲主查詢或外查詢

分類:
按子查詢出現的位置:
	select後面:
		僅僅支持標量子查詢
	
	from後面:
		支持表子查詢
	where或having後面:★
		支持標量子查詢(單行子查詢) √
		支持列子查詢  (多行子查詢) √
		
		支持行子查詢
		
	exists後面(相關子查詢)
		表子查詢
按結果集的行列數不同:
	標量子查詢(結果集只有一行一列)
	列子查詢(結果集只有一列多行)
	行子查詢(結果集有一行多列)
	表子查詢(結果集一般爲多行多列)
*/

#一、where或having後面
/*
1、標量子查詢(單行子查詢)
2、列子查詢(多行子查詢)

3、行子查詢(多列多行)

特點:
①子查詢放在小括號內
②子查詢一般放在條件的右側
③標量子查詢,一般搭配着單行操作符使用
> < >= <= = <>

列子查詢,一般搭配着多行操作符使用
in、any/some、all

④子查詢的執行優先於主查詢執行,主查詢的條件用到了子查詢的結果

*/
#1.標量子查詢★

#案例1:誰的工資比 Abel 高?

#①查詢Abel的工資      標量子查詢
SELECT salary
FROM employees
WHERE last_name = 'Abel'

#②查詢員工的信息,滿足 salary>①結果
SELECT *
FROM employees
WHERE salary>(

	SELECT salary
	FROM employees
	WHERE last_name = 'Abel'

);

#案例2:返回job_id與141號員工相同,salary比143號員工多的員工 姓名,job_id 和工資

#①查詢141號員工的job_id
SELECT job_id
FROM employees
WHERE employee_id = 141

#②查詢143號員工的salary
SELECT salary
FROM employees
WHERE employee_id = 143

#③查詢員工的姓名,job_id 和工資,要求job_id=①並且salary>②

SELECT last_name,job_id,salary
FROM employees
WHERE job_id = (
	SELECT job_id
	FROM employees
	WHERE employee_id = 141
) AND salary>(
	SELECT salary
	FROM employees
	WHERE employee_id = 143

);


#案例3:返回公司工資最少的員工的last_name,job_id和salary

#①查詢公司的 最低工資
SELECT MIN(salary)
FROM employees

#②查詢last_name,job_id和salary,要求salary=①
SELECT last_name,job_id,salary
FROM employees
WHERE salary=(
	SELECT MIN(salary)
	FROM employees
);


#案例4:查詢最低工資大於50號部門最低工資的部門id和其最低工資

#①查詢50號部門的最低工資
SELECT  MIN(salary)
FROM employees
WHERE department_id = 50

#②查詢每個部門的最低工資

SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id

#③ 在②基礎上篩選,滿足min(salary)>①
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
	SELECT  MIN(salary)
	FROM employees
	WHERE department_id = 50


);

#非法使用標量子查詢

SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
	SELECT  salary
	FROM employees
	WHERE department_id = 50


);


SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
	SELECT  salary
	FROM employees
	WHERE department_id = 250


);


#2.列子查詢(多行子查詢)★
#案例1:返回location_id是1400或1700的部門中的所有員工姓名

#①查詢location_id是1400或1700的部門編號
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)

#②查詢員工姓名,要求部門號是①列表中的某一個

SELECT last_name
FROM employees
WHERE department_id  IN(
	SELECT DISTINCT department_id
	FROM departments
	WHERE location_id IN(1400,1700)


);


SELECT last_name
FROM employees
WHERE department_id  <>ALL(
	SELECT DISTINCT department_id
	FROM departments
	WHERE location_id IN(1400,1700)


);


#案例2:返回其它工種中比job_id爲‘IT_PROG’工種任一工資低的員工的員工號、姓名、job_id 以及salary

#①查詢job_id爲‘IT_PROG’部門任一工資

SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'

#②查詢員工號、姓名、job_id 以及salary,salary<(①)的任意一個
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ANY(
	SELECT DISTINCT salary
	FROM employees
	WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';

#或
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<(
	SELECT MAX(salary)
	FROM employees
	WHERE job_id = 'IT_PROG'

) AND job_id <> 'IT_PROG';


#案例3:返回其它部門中比job_id爲‘IT_PROG’部門所有工資都低的員工   的員工號、姓名、job_id 以及salary

SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ALL(
	SELECT DISTINCT salary
	FROM employees
	WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';

#或

SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<(
	SELECT MIN( salary)
	FROM employees
	WHERE job_id = 'IT_PROG'

) AND job_id<>'IT_PROG';



#3、行子查詢(結果集一行多列或多行多列)

#案例:查詢員工編號最小並且工資最高的員工信息
SELECT * 
FROM employees
WHERE (employee_id,salary)=(
	SELECT MIN(employee_id),MAX(salary)
	FROM employees
);

#①查詢最小的員工編號
SELECT MIN(employee_id)
FROM employees


#②查詢最高工資
SELECT MAX(salary)
FROM employees


#③查詢員工信息
SELECT *
FROM employees
WHERE employee_id=(
	SELECT MIN(employee_id)
	FROM employees


)AND salary=(
	SELECT MAX(salary)
	FROM employees

);


#二、select後面
/*
僅僅支持標量子查詢
*/

#案例:查詢每個部門的員工個數
SELECT d.*,(

	SELECT COUNT(*)
	FROM employees e
	WHERE e.department_id = d.`department_id`
 ) 個數
 FROM departments d;
 
 
 #案例2:查詢員工號=102的部門名
 
SELECT (
	SELECT department_name
	FROM departments d
	INNER JOIN employees e
	ON d.department_id=e.department_id
	WHERE e.employee_id=102
	
) 部門名;

#只能是一行一列
SELECT (
	SELECT department_name,e.department_id
	FROM departments d
	INNER JOIN employees e
	ON d.department_id=e.department_id
	WHERE e.employee_id=102
	
) 部門名;

#三、from後面
/*
將子查詢結果充當一張表,要求必須起別名
*/

#案例:查詢每個部門的平均工資的工資等級
#①查詢每個部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id


SELECT * FROM job_grades;


#②連接①的結果集和job_grades表,篩選條件平均工資 between lowest_sal and highest_sal

SELECT  ag_dep.*,g.`grade_level`
FROM (
	SELECT AVG(salary) ag,department_id
	FROM employees
	GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;



#四、exists後面(相關子查詢)

/*
語法:
exists(完整的查詢語句)
結果:
1或0
*/

SELECT EXISTS(SELECT employee_id FROM employees WHERE salary=300000);

#案例1:查詢有員工的部門名

#in
SELECT department_name
FROM departments d
WHERE d.`department_id` IN(
	SELECT department_id
	FROM employees

)

#exists

SELECT department_name
FROM departments d
WHERE EXISTS(
	SELECT *
	FROM employees e
	WHERE d.`department_id`=e.`department_id`


);
#案例2:查詢沒有女朋友的男神信息

#in
SELECT bo.*
FROM boys bo
WHERE bo.id NOT IN(
	SELECT boyfriend_id
	FROM beauty
)

#exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
	SELECT boyfriend_id
	FROM beauty b
	WHERE bo.`id`=b.`boyfriend_id`

);

#子查詢案例講解

#1.	查詢和Zlotkey相同部門的員工姓名和工資

#①查詢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 last_name,employee_id,salary
FROM employees
WHERE salary>(

	SELECT AVG(salary)
	FROM employees
);



#3.查詢各部門中工資比本部門平均工資高的員工的員工號, 姓名和工資
#①查詢各部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id

#②連接①結果集和employees表,進行篩選
SELECT employee_id,last_name,salary,e.department_id
FROM employees e
INNER JOIN (
	SELECT AVG(salary) ag,department_id
	FROM employees
	GROUP BY department_id


) ag_dep
ON e.department_id = ag_dep.department_id
WHERE salary>ag_dep.ag ;



#4.查詢和姓名中包含字母u的員工在相同部門的員工的員工號和姓名
#①查詢姓名中包含字母u的員工的部門

SELECT  DISTINCT department_id
FROM employees
WHERE last_name LIKE '%u%'

#②查詢部門號=①中的任意一個的員工號和姓名
SELECT last_name,employee_id
FROM employees
WHERE department_id IN(
	SELECT  DISTINCT department_id
	FROM employees
	WHERE last_name LIKE '%u%'
);


#5. 查詢在部門的location_id爲1700的部門工作的員工的員工號

#①查詢location_id爲1700的部門

SELECT DISTINCT department_id
FROM departments 
WHERE location_id  = 1700


#②查詢部門號=①中的任意一個的員工號
SELECT employee_id
FROM employees
WHERE department_id =ANY(  # =ANY 等價於IN
	SELECT DISTINCT department_id
	FROM departments 
	WHERE location_id  = 1700

);
#6.查詢管理者是King的員工姓名和工資

#①查詢姓名爲king的員工編號
SELECT employee_id
FROM employees
WHERE last_name  = 'K_ing'

#②查詢哪個員工的manager_id = ①
SELECT last_name,salary
FROM employees
WHERE manager_id IN(
	SELECT employee_id
	FROM employees
	WHERE last_name  = 'K_ing'

);

#7.查詢工資最高的員工的姓名,要求first_name和last_name顯示爲一列,列名爲 姓.名

#①查詢最高工資
SELECT MAX(salary)
FROM employees

#②查詢工資=①的姓.名

SELECT CONCAT(first_name,last_name) "姓.名"
FROM employees
WHERE salary=(
	SELECT MAX(salary)
	FROM employees

);

###=======================================================================================
#進階8:分頁查詢 ★
/*

應用場景:當要顯示的數據,一頁顯示不全,需要分頁提交sql請求
語法:
	select 查詢列表          7
	from 表                  1
	【join type join 表2     2
	on 連接條件              3
	where 篩選條件            4
	group by 分組字段         5
	having 分組後的篩選        6
	order by 排序的字段】       8
	limit offset,size;          9              執行順序
	
	offset要顯示條目的起始索引(起始索引從0開始)
	size 要顯示的條目個數
特點:
	①limit語句放在查詢語句的最後
	②公式
	要顯示的頁數 page,每頁的條目數size
	
	select 查詢列表
	from 表
	limit (page-1)*size,size;
	
	size=10
	page  
	1	0
	2  	10
	3	20
	
*/
#案例1:查詢前五條員工信息

SELECT * FROM  employees LIMIT 0,5;
SELECT * FROM  employees LIMIT 5;

#案例2:查詢第11條——第25條
SELECT * FROM  employees LIMIT 10,15;

#案例3:有獎金的員工信息,並且工資較高的前10名顯示出來
SELECT 
    * 
FROM
    employees 
WHERE commission_pct IS NOT NULL 
ORDER BY salary DESC 
LIMIT 10 ;

/*
已知表stuinfo
id 學號
name 姓名
email 郵箱 [email protected]
gradeId  年級編號
sex  性別,男,女
age 年齡

已知表grade
id  年紀編號
gradeName 年級名稱
*/
# 1查詢所有學員的的郵箱的用戶名(注:郵箱中@前面的字符)
SELECT SUBSTR(email,1,INSTR(email,'@'-1)) 用戶名
FROM stuinfo

# 2查詢男生和女生的個數
SELECT COUNT(*) 個數,sex
FROM stuinfo
GROUP BY sex;

# 3查詢年領>18歲的所有學生的姓名和年級名稱
SELECT 'name',gradeName
FROM stuinfo s
INNER JOIN grade g ON s.gradeId = g.id
WHERE age > 18

# 4查詢那個年紀的學生最小年齡>20歲
1.每個年級的最小年齡
SELECT MIN(age),gradeid
FROM stuinfo
GROUP BY gradeid
2.在1的結果上進行篩選
SELECT MIN(age),gradeid
FROM stuinfo
GROUP BY gradeid
HAVING MIN(age) > 20;

# 5試說出查詢語句中涉及到的所有的關鍵字,以及執行的先後順序
	SELECT 查詢列表          7
	FROM 表                  1
	【JOIN TYPE JOIN 表2     2
	ON 連接條件              3
	WHERE 篩選條件            4
	GROUP BY 分組字段         5
	HAVING 分組後的篩選        6
	ORDER BY 排序的字段】       8
	LIMIT OFFSET,size;          9              執行順序

#子查詢經典案例講解
# 1. 查詢工資最低的員工信息: last_name, salary

#①查詢最低的工資
SELECT MIN(salary)
FROM employees

#②查詢last_name,salary,要求salary=①
SELECT last_name,salary
FROM employees
WHERE salary=(
	SELECT MIN(salary)
	FROM employees
);

# 2. 查詢平均工資最低的部門信息

#方式一:
#①各部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#②查詢①結果上的最低平均工資
SELECT MIN(ag)
FROM (
	SELECT AVG(salary) ag,department_id
	FROM employees
	GROUP BY department_id
) ag_dep

#③查詢哪個部門的平均工資=②

SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
	SELECT MIN(ag)
	FROM (
		SELECT AVG(salary) ag,department_id
		FROM employees
		GROUP BY department_id
	) ag_dep

);

#④查詢部門信息

SELECT d.*
FROM departments d
WHERE d.`department_id`=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	HAVING AVG(salary)=(
		SELECT MIN(ag)
		FROM (
			SELECT AVG(salary) ag,department_id
			FROM employees
			GROUP BY department_id
		) ag_dep

	)

);

#方式二:
#①各部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id

#②求出最低平均工資的部門編號
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) 
LIMIT 1;

#③查詢部門信息
SELECT *
FROM departments
WHERE department_id=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary) 
	LIMIT 1
);




# 3. 查詢平均工資最低的部門信息和該部門的平均工資
#①各部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#②求出最低平均工資的部門編號
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) 
LIMIT 1;
#③查詢部門信息
SELECT d.*,ag
FROM departments d
JOIN (
	SELECT AVG(salary) ag,department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary) 
	LIMIT 1

) ag_dep
ON d.`department_id`=ag_dep.department_id;



# 4. 查詢平均工資最高的 job 信息
#①查詢最高的job的平均工資
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1

#②查詢job信息
SELECT * 
FROM jobs
WHERE job_id=(
	SELECT job_id
	FROM employees
	GROUP BY job_id
	ORDER BY AVG(salary) DESC
	LIMIT 1

);
# 5. 查詢平均工資高於公司平均工資的部門有哪些?

#①查詢平均工資
SELECT AVG(salary)
FROM employees

#②查詢每個部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id

#③篩選②結果集,滿足平均工資>①

SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)>(
	SELECT AVG(salary)
	FROM employees

);

# 6. 查詢出公司中所有 manager 的詳細信息.
#①查詢所有manager的員工編號
SELECT DISTINCT manager_id
FROM employees

#②查詢詳細信息,滿足employee_id=①
SELECT *
FROM employees
WHERE employee_id =ANY(
	SELECT DISTINCT manager_id
	FROM employees

);

# 7. 各個部門中 最高工資中最低的那個部門的 最低工資是多少

#①查詢各部門的最高工資中最低的部門編號
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY MAX(salary)
LIMIT 1


#②查詢①結果的那個部門的最低工資

SELECT MIN(salary) ,department_id
FROM employees
WHERE department_id=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY MAX(salary)
	LIMIT 1


);
# 8. 查詢平均工資最高的部門的 manager 的詳細信息: last_name, department_id, email, salary
#①查詢平均工資最高的部門編號
SELECT 
    department_id 
FROM
    employees 
GROUP BY department_id 
ORDER BY AVG(salary) DESC 
LIMIT 1 

#②將employees和departments連接查詢,篩選條件是①
    SELECT 
        last_name, d.department_id, email, salary 
    FROM
        employees e 
        INNER JOIN departments d 
            ON d.manager_id = e.employee_id 
    WHERE d.department_id = 
        (SELECT 
            department_id 
        FROM
            employees 
        GROUP BY department_id 
        ORDER BY AVG(salary) DESC 
        LIMIT 1) ;


#作業講解
#一、查詢每個專業的學生人數
SELECT majorid,COUNT(*)
FROM student
GROUP BY majorid;

#二、查詢參加考試的學生中,每個學生的平均分、最高分
SELECT AVG(score),MAX(score),studentno
FROM result
GROUP BY studentno;

#三、查詢姓張的每個學生的最低分大於60的學號、姓名
SELECT s.studentno,s.`studentname`,MIN(score)
FROM student s
JOIN result r
ON s.`studentno`=r.`studentno`
WHERE s.`studentname` LIKE '張%'
GROUP BY s.`studentno`
HAVING MIN(score)>60;
#四、查詢每個專業生日在“1988-1-1”後的學生姓名、專業名稱

SELECT m.`majorname`,s.`studentname`
FROM student s
JOIN major m
ON m.`majorid`=s.`majorid`
WHERE DATEDIFF(borndate,'1988-1-1')>0
GROUP BY m.`majorid`;


#五、查詢每個專業的男生人數和女生人數分別是多少

SELECT COUNT(*),sex,majorid
FROM student
GROUP BY sex,majorid;
#六、查詢專業和張翠山一樣的學生的最低分
#①查詢張翠山的專業編號
SELECT majorid
FROM student
WHERE studentname = '張翠山'

#②查詢編號=①的所有學生編號
SELECT studentno
FROM student
WHERE majorid=(
	SELECT majorid
	FROM student
	WHERE studentname = '張翠山'

)
#②查詢最低分
SELECT MIN(score)
FROM result
WHERE studentno IN(

	SELECT studentno
	FROM student
	WHERE majorid=(
		SELECT majorid
		FROM student
		WHERE studentname = '張翠山'

	)
)

#七、查詢大於60分的學生的姓名、密碼、專業名

SELECT studentname,loginpwd,majorname
FROM student s
JOIN major m ON s.majorid=  m.majorid
JOIN result r ON s.studentno=r.studentno
WHERE r.score>60;
#八、按郵箱位數分組,查詢每組的學生個數
SELECT COUNT(*),LENGTH(email)
FROM student
GROUP BY LENGTH(email);
#九、查詢學生名、專業名、分數

SELECT studentname,score,majorname
FROM student s
JOIN major m ON s.majorid=  m.majorid
LEFT JOIN result r ON s.studentno=r.studentno


#十、查詢哪個專業沒有學生,分別用左連接和右連接實現
#左
SELECT m.`majorid`,m.`majorname`,s.`studentno`
FROM major m
LEFT JOIN student s ON m.`majorid` = s.`majorid`
WHERE s.`studentno` IS NULL;

#右
SELECT m.`majorid`,m.`majorname`,s.`studentno`
FROM student s
RIGHT JOIN  major m ON m.`majorid` = s.`majorid`
WHERE s.`studentno` IS NULL;
#十一、查詢沒有成績的學生人數

SELECT COUNT(*)
FROM student s
LEFT JOIN result r ON s.`studentno` = r.`studentno`
WHERE r.`id` IS NULL



########=======================================================================
#進階9:聯合查詢
/*
union 聯合 合併:將多條查詢語句的結果合併成一個結果

語法:
查詢語句1
union
查詢語句2
union
...


應用場景:
要查詢的結果來自於多個表,且多個表沒有直接的連接關係,但查詢的信息一致時

特點:★
1、要求多條查詢語句的查詢列數是一致的!
2、要求多條查詢語句的查詢的每一列的類型和順序最好一致
3、union關鍵字默認去重,如果使用union all 可以包含重複項

*/


#引入的案例:查詢部門編號>90或郵箱包含a的員工信息

SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;

SELECT * FROM employees  WHERE email LIKE '%a%'
UNION
SELECT * FROM employees  WHERE department_id>90;

#案例:查詢中國用戶中男性的信息以及外國用戶中年男性的用戶信息
SELECT id,cname FROM t_ca WHERE csex='男'
UNION 
SELECT t_id,tname FROM t_ua WHERE tGender='male';

SELECT id,cname FROM t_ca WHERE csex='男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender='male';


#===========================================================
#DML語言
#DML語言
/*
數據操作語言:
插入:insert
修改:update
刪除:delete

*/

#一、插入語句
#方式一:經典的插入
/*
語法:
insert into 表名(列名,...) values(值1,...);

*/
SELECT * FROM beauty;
#1.插入的值的類型要與列的類型一致或兼容
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐藝昕','女','1990-4-23','1898888888',NULL,2);

#2.不可以爲null的列必須插入值。可以爲null的列如何插入值?
#方式一:
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐藝昕','女','1990-4-23','1898888888',NULL,2);

#方式二:

INSERT INTO beauty(id,NAME,sex,phone)
VALUES(15,'娜扎','女','1388888888');


#3.列的順序是否可以調換
INSERT INTO beauty(NAME,sex,id,phone)
VALUES('蔣欣','女',16,'110');


#4.列數和值的個數必須一致

INSERT INTO beauty(NAME,sex,id,phone)
VALUES('關曉彤','女',17,'110');

#5.可以省略列名,默認所有列,而且列的順序和表中列的順序一致

INSERT INTO beauty
VALUES(18,'張飛','男',NULL,'119',NULL,NULL);

#方式二:
/*

語法:
insert into 表名
set 列名=值,列名=值,...
*/


INSERT INTO beauty
SET id=19,NAME='劉濤',phone='999';


#兩種方式大pk ★


#1、方式一支持插入多行,方式二不支持

INSERT INTO beauty
VALUES(23,'唐藝昕1','女','1990-4-23','1898888888',NULL,2)
,(24,'唐藝昕2','女','1990-4-23','1898888888',NULL,2)
,(25,'唐藝昕3','女','1990-4-23','1898888888',NULL,2);

#2、方式一支持子查詢,方式二不支持

INSERT INTO beauty(id,NAME,phone)
SELECT 26,'宋茜','11809866';

INSERT INTO beauty(id,NAME,phone)
SELECT id,boyname,'1234567'
FROM boys WHERE id<3;

#二、修改語句

/*

1.修改單表的記錄★

語法:
update 表名             1
set 列=新值,列=新值,...  3
where 篩選條件;          2

2.修改多表的記錄【補充】

語法:
sql92語法:
update 表1 別名,表2 別名
set 列=值,...
where 連接條件
and 篩選條件;

sql99語法:
update 表1 別名
inner|left|right join 表2 別名
on 連接條件
set 列=值,...
where 篩選條件;


*/


#1.修改單表的記錄
#案例1:修改beauty表中姓唐的女神的電話爲13899888899

UPDATE beauty SET phone = '13899888899'
WHERE NAME LIKE '唐%';

#案例2:修改boys表中id好爲2的名稱爲張飛,魅力值 10
UPDATE boys SET boyname='張飛',usercp=10
WHERE id=2;



#2.修改多表的記錄

#案例 1:修改張無忌的女朋友的手機號爲114

UPDATE boys bo
INNER JOIN beauty b ON bo.`id`=b.`boyfriend_id`
SET b.`phone`='119'
WHERE bo.`boyName`='張無忌';



#案例2:修改沒有男朋友的女神的男朋友編號都爲2號

UPDATE boys bo
RIGHT JOIN beauty b ON bo.`id`=b.`boyfriend_id`
SET b.`boyfriend_id`=2
WHERE bo.`id` IS NULL;

SELECT * FROM boys;


#三、刪除語句
/*

方式一:delete
語法:

1、單表的刪除【★】     刪除的是一行的數據
delete from 表名 where 篩選條件

2、多表的刪除【補充】

sql92語法:
delete 表1的別名,表2的別名
from 表1 別名,表2 別名
where 連接條件
and 篩選條件;

sql99語法:

delete 表1的別名,表2的別名
from 表1 別名
inner|left|right join 表2 別名 on 連接條件
where 篩選條件;



方式二:truncate   刪除表的全部數據
語法:truncate table 表名;

*/

#方式一:delete
#1.單表的刪除
#案例:刪除手機號以9結尾的女神信息

DELETE FROM beauty WHERE phone LIKE '%9';
SELECT * FROM beauty;


#2.多表的刪除

#案例:刪除張無忌的女朋友的信息

DELETE b
FROM beauty b
INNER JOIN boys bo ON b.`boyfriend_id` = bo.`id`
WHERE bo.`boyName`='張無忌';


#案例:刪除黃曉明的信息以及他女朋友的信息
DELETE b,bo
FROM beauty b
INNER JOIN boys bo ON b.`boyfriend_id`=bo.`id`
WHERE bo.`boyName`='黃曉明';



#方式二:truncate語句

#案例:將魅力值>100的男神信息刪除
TRUNCATE TABLE boys ;



#delete pk truncate【面試題★】

/*

1.delete 可以加where 條件,truncate不能加

2.truncate刪除,效率高一丟丟
3.假如要刪除的表中有自增長列,
如果用delete刪除後,再插入數據,自增長列的值從斷點開始,
而truncate刪除後,再插入數據,自增長列的值從1開始。
4.truncate刪除沒有返回值,delete刪除有返回值

5.truncate刪除不能回滾,delete刪除可以回滾.

*/

SELECT * FROM boys;

DELETE FROM boys;
TRUNCATE TABLE boys;
INSERT INTO boys (boyname,usercp)
VALUES('張飛',100),('劉備',100),('關雲長',100);


#案例講解
#1.運行以下腳本創建表my_employees

USE myemployees;
CREATE TABLE my_employees(
	Id INT(10),
	First_name VARCHAR(10),
	Last_name VARCHAR(10),
	Userid VARCHAR(10),
	Salary DOUBLE(10,2)
);
CREATE TABLE users(
	id INT,
	userid VARCHAR(10),
	department_id INT

);
#2.顯示錶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

#方式一:
INSERT INTO my_employees
VALUES(1,'patel','Ralph','Rpatel',895),
(2,'Dancs','Betty','Bdancs',860),
(3,'Biri','Ben','Bbiri',1100),
(4,'Newman','Chad','Cnewman',750),
(5,'Ropeburn','Audrey','Aropebur',1550);

DELETE FROM my_employees;
#方式二:

INSERT INTO my_employees
SELECT 1,'patel','Ralph','Rpatel',895 UNION
SELECT 2,'Dancs','Betty','Bdancs',860 UNION
SELECT 3,'Biri','Ben','Bbiri',1100 UNION
SELECT 4,'Newman','Chad','Cnewman',750 UNION
SELECT 5,'Ropeburn','Audrey','Aropebur',1550;

				
#4.向users表中插入數據
1	Rpatel	10
2	Bdancs	10
3	Bbiri	20
4	Cnewman	30
5	Aropebur	40

INSERT INTO users
VALUES(1,'Rpatel',10),
(2,'Bdancs',10),
(3,'Bbiri',20);




#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的user表和my_employees表的記錄全部刪除

DELETE u,e
FROM users u
JOIN my_employees e ON u.`userid`=e.`Userid`
WHERE u.`userid`='Bbiri';

#8.刪除所有數據

DELETE FROM my_employees;
DELETE FROM users;
#9.檢查所作的修正

SELECT * FROM my_employees;
SELECT * FROM users;

#10.清空表my_employees
TRUNCATE TABLE my_employees;

#==============================================================
#DDL
/*

數據定義語言

庫和表的管理

一、庫的管理
創建、修改、刪除
二、表的管理
創建、修改、刪除

創建: create
修改: alter
刪除: drop

*/

#一、庫的管理
#1、庫的創建
/*
語法:
create database  [if not exists]庫名;
*/


#案例:創建庫Books

CREATE DATABASE IF NOT EXISTS books ;


#2、庫的修改

RENAME DATABASE books TO 新庫名;

#更改庫的字符集

ALTER DATABASE books CHARACTER SET gbk;


#3、庫的刪除

DROP DATABASE IF EXISTS books;




#二、表的管理
#1.表的創建 ★

/*
語法:
create table 表名(
	列名 列的類型【(長度) 約束】,
	列名 列的類型【(長度) 約束】,
	列名 列的類型【(長度) 約束】,
	...
	列名 列的類型【(長度) 約束】


)


*/
#案例:創建表Book

CREATE TABLE book(
	id INT,#編號
	bName VARCHAR(20),#圖書名
	price DOUBLE,#價格
	authorId  INT,#作者編號
	publishDate DATETIME#出版日期



);


DESC book;

#案例:創建表author
CREATE TABLE IF NOT EXISTS author(
	id INT,
	au_name VARCHAR(20),
	nation VARCHAR(10)

)
DESC author;


#2.表的修改

/*
語法
alter table 表名 add|drop|modify|change column 列名 【列類型 約束】;

*/

#①修改列名
ALTER TABLE 表名 ADD|DROP|MODIFY|CHANGE COLUMN 列名 列類型
ALTER TABLE book CHANGE COLUMN publishdate pubDate DATETIME;


#②修改列的類型或約束                     列類型
ALTER TABLE book MODIFY COLUMN pubdate TIMESTAMP;

#③添加新列				列類型
ALTER TABLE author ADD COLUMN annual DOUBLE; 

#④刪除列

ALTER TABLE book_author DROP COLUMN  annual;
#⑤修改表名

ALTER TABLE author RENAME TO book_author;

DESC book;




#3.表的刪除

DROP TABLE IF EXISTS book_author;

SHOW TABLES;


#通用的寫法:

DROP DATABASE IF EXISTS 舊庫名;
CREATE DATABASE 新庫名;


DROP TABLE IF EXISTS 舊錶名;
CREATE TABLE  表名();



#4.表的複製

INSERT INTO author VALUES
(1,'村上春樹','日本'),
(2,'莫言','中國'),
(3,'馮唐','中國'),
(4,'金庸','中國');

SELECT * FROM Author;
SELECT * FROM copy2;
#1.僅僅複製表的結構

CREATE TABLE copy LIKE author;

#2.複製表的結構+數據
CREATE TABLE copy2 
SELECT * FROM author;

#只複製部分數據
CREATE TABLE copy3
SELECT id,au_name
FROM author 
WHERE nation='中國';


#僅僅複製某些字段

CREATE TABLE copy4 
SELECT id,au_name
FROM author
WHERE 0;

#案例講解
#1.	創建表dept1
NAME	NULL?	TYPE
id		INT(7)
NAME		VARCHAR(25)


USE test;

CREATE TABLE dept1(
	id INT(7),
	NAME VARCHAR(25)
	

);
#2.	將表departments中的數據插入新表dept2中

CREATE TABLE dept2
SELECT department_id,department_name
FROM myemployees.departments;


#3.	創建表emp5
NAME	NULL?	TYPE
id		INT(7)
First_name	VARCHAR (25)
Last_name	VARCHAR(25)
Dept_id		INT(7)

CREATE TABLE emp5(
id INT(7),
first_name VARCHAR(25),
last_name VARCHAR(25),
dept_id INT(7)

);


#4.	將列Last_name的長度增加到50

ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);
#5.	根據表employees創建employees2

CREATE TABLE employees2 LIKE myemployees.employees;

#6.	刪除表emp5
DROP TABLE IF EXISTS emp5;

#7.	將表employees2重命名爲emp5

ALTER TABLE employees2 RENAME TO emp5;

#8.在表dept和emp5中添加新列test_column,並檢查所作的操作

ALTER TABLE emp5 ADD COLUMN test_column INT;
#9.直接刪除表emp5中的列 dept_id
DESC emp5;
ALTER TABLE emp5 DROP COLUMN test_column;

#數據類型介紹
#常見的數據類型
/*
數值型:
	整型
	小數:
		定點數
		浮點數
字符型:
	較短的文本:char、varchar
	較長的文本:text、         blob(保存較長的二進制數據)

日期型:

*/

#一、整型
/*
分類:
tinyint、smallint、mediumint、int/integer、bigint
1	 2		3	4		8

特點:
① 如果不設置無符號還是有符號,默認是有符號,如果想設置無符號,需要添加unsigned關鍵字
② 如果插入的數值超出了整型的範圍,會報out of range異常,並且插入臨界值
③ 如果不設置長度,會有默認的長度
長度代表了顯示的最大寬度,如果不夠會用0在左邊填充,但必須搭配zerofill使用!

*/

#1.如何設置無符號和有符號

DROP TABLE IF EXISTS tab_int;
CREATE TABLE tab_int(
	t1 INT(7) ZEROFILL,
	t2 INT(7) ZEROFILL 

);

DESC tab_int;


INSERT INTO tab_int VALUES(-123456);
INSERT INTO tab_int VALUES(-123456,-123456);
INSERT INTO tab_int VALUES(2147483648,4294967296);

INSERT INTO tab_int VALUES(123,123);


SELECT * FROM tab_int;


#二、小數
/*
分類:
1.浮點型
float(M,D)
double(M,D)
2.定點型
dec(M,D)
decimal(M,D)

特點:

①
M:整數部位+小數部位
D:小數部位
如果超過範圍,則插入臨界值

②
M和D都可以省略
如果是decimal,則M默認爲10,D默認爲0
如果是float和double,則會根據插入的數值的精度來決定精度

③定點型的精確度較高,如果要求插入數值的精度較高如貨幣運算等則考慮使用


*/
#測試M和D

DROP TABLE tab_float;
CREATE TABLE tab_float(
	f1 FLOAT,
	f2 DOUBLE,
	f3 DECIMAL
);
SELECT * FROM tab_float;
DESC tab_float;

INSERT INTO tab_float VALUES(123.4523,123.4523,123.4523);
INSERT INTO tab_float VALUES(123.456,123.456,123.456);
INSERT INTO tab_float VALUES(123.4,123.4,123.4);
INSERT INTO tab_float VALUES(1523.4,1523.4,1523.4);



#原則:
/*
所選擇的類型越簡單越好,能保存數值的類型越小越好

*/

#三、字符型
/*
較短的文本:

char
varchar

其他:

binary和varbinary用於保存較短的二進制
enum用於保存枚舉
set用於保存集合


較長的文本:
text
blob(較大的二進制)

特點:



	寫法		M的意思					特點			空間的耗費	效率
char	char(M)		最大的字符數,可以省略,默認爲1		固定長度的字符		比較耗費	高

varchar varchar(M)	最大的字符數,不可以省略		可變長度的字符		比較節省	低
*/



CREATE TABLE tab_char(
	c1 ENUM('a','b','c')


);


INSERT INTO tab_char VALUES('a');
INSERT INTO tab_char VALUES('b');
INSERT INTO tab_char VALUES('c');
INSERT INTO tab_char VALUES('m');
INSERT INTO tab_char VALUES('A');

SELECT * FROM tab_set;



CREATE TABLE tab_set(

	s1 SET('a','b','c','d')



);
INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('A,B');
INSERT INTO tab_set VALUES('a,c,d');


#四、日期型

/*

分類:
date只保存日期
time 只保存時間
year只保存年

datetime保存日期+時間
timestamp保存日期+時間 


特點:

		字節		範圍		時區等的影響
datetime       8		1000——9999	       不受
timestamp	4	        1970-2038	       受

*/


CREATE TABLE tab_date(
	t1 DATETIME,
	t2 TIMESTAMP

);
INSERT INTO tab_date VALUES(NOW(),NOW());
SELECT * FROM tab_date;
SHOW VARIABLES LIKE 'time_zone';

SET time_zone='+9:00';


#=====================================================================================
#常見約束

/*


含義:一種限制,用於限制表中的數據,爲了保證表中的數據的準確和可靠性


分類:六大約束
	NOT NULL:非空,用於保證該字段的值不能爲空
	比如姓名、學號等
	DEFAULT:默認,用於保證該字段有默認值
	比如性別
	PRIMARY KEY:主鍵,用於保證該字段的值具有唯一性,並且非空
	比如學號、員工編號等
	UNIQUE:唯一,用於保證該字段的值具有唯一性,可以爲空
	比如座位號
	CHECK:檢查約束【mysql中不支持】
	比如年齡、性別
	FOREIGN KEY:外鍵,用於限制兩個表的關係,用於保證該字段的值必須來自於主表的關聯列的值
		在從表添加外鍵約束,用於引用主表中某列的值
	比如學生表的專業編號,員工表的部門編號,員工表的工種編號
	

添加約束的時機:
	1.創建表時
	2.修改表時
	

約束的添加分類:
	列級約束:
		六大約束語法上都支持,但外鍵約束沒有效果
		
	表級約束:
		
		除了非空、默認,其他的都支持
	
	CREATE TABLE 表名(
	字段名 字段類型 列級約束,
	字段名 字段類型,
	表級約束

)
		
主鍵和唯一的大對比:

		保證唯一性  是否允許爲空    一個表中可以有多少個   是否允許組合
	主鍵	√		×		至多有1個           √,但不推薦
	唯一	√		√		可以有多個          √,但不推薦
外鍵:
	1、要求在從表設置外鍵關係
	2、從表的外鍵列的類型和主表的關聯列的類型要求一致或兼容,名稱無要求
	3、主表的關聯列必須是一個key(一般是主鍵或唯一)
	4、插入數據時,先插入主表,再插入從表
	刪除數據時,先刪除從表,再刪除主表


*/

CREATE TABLE 表名(
	字段名 字段類型 列級約束,
	字段名 字段類型,
	表級約束

)
CREATE DATABASE students;
#一、創建表時添加約束

#1.添加列級約束
/*
語法:

直接在字段名和類型後面追加 約束類型即可。

只支持:默認、非空、主鍵、唯一



*/

USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo(
	id INT PRIMARY KEY,#主鍵
	stuName VARCHAR(20) NOT NULL UNIQUE,#非空       可以添加多個約束
	gender CHAR(1) CHECK(gender='男' OR gender ='女'),#檢查
	seat INT UNIQUE,#唯一
	age INT DEFAULT  18,#默認約束
	majorId INT REFERENCES major(id)#外鍵   學員信息表引用major(id)

);


CREATE TABLE major(
	id INT PRIMARY KEY,
	majorName VARCHAR(20)
);

DESC stuinfo;

#查看stuinfo中的所有索引,包括主鍵、外鍵、唯一
SHOW INDEX FROM stuinfo;


#2.添加表級約束
/*

語法:在各個字段的最下面
 【constraint 約束名】 約束類型(字段名) 
*/

DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuname VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT,
	
	CONSTRAINT pk PRIMARY KEY(id),#主鍵 pk名字隨便起
	CONSTRAINT uq UNIQUE(seat),#唯一鍵
	CONSTRAINT ck CHECK(gender ='男' OR gender  = '女'),#檢查
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)#外鍵
	
);

#可以不起名
CREATE TABLE stuinfo(
	id INT,
	stuname VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT,
	
	PRIMARY KEY(id),#主鍵 pk名字隨便起
	UNIQUE(seat),#唯一鍵
	CHECK(gender ='男' OR gender  = '女'),#檢查
	FOREIGN KEY(majorid) REFERENCES major(id)#外鍵
	
);


SHOW INDEX FROM stuinfo;



#通用的寫法:★           一個表可以添加多個外鍵

CREATE TABLE IF NOT EXISTS stuinfo(
	id INT PRIMARY KEY,
	stuname VARCHAR(20) NOT NULL,
	sex CHAR(1),
	age INT DEFAULT 18,
	seat INT UNIQUE,
	majorid INT,
	CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)

);



#二、修改表時添加約束

/*
1、添加列級約束
alter table 表名 modify column 字段名 字段類型 新約束;

2、添加表級約束
alter table 表名 add 【constraint 約束名】 約束類型(字段名) 【外鍵的引用】;


*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuname VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT
)
DESC stuinfo;
#1.添加非空約束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20)  NOT NULL;
#2.添加默認約束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
#3.添加主鍵
#①列級約束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
#②表級約束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

#4.添加唯一

#①列級約束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
#②表級約束
ALTER TABLE stuinfo ADD UNIQUE(seat);


#5.添加外鍵            添加表級約束
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id); 

#三、修改表時刪除約束

#1.刪除非空約束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;

#2.刪除默認約束
ALTER TABLE stuinfo MODIFY COLUMN age INT ;

#3.刪除主鍵
ALTER TABLE stuinfo DROP PRIMARY KEY;

#4.刪除唯一
ALTER TABLE stuinfo DROP INDEX seat;

#5.刪除外鍵
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;

SHOW INDEX FROM stuinfo;

#常見約束講解

#1.向表emp2的id列中添加PRIMARY KEY約束(my_emp_id_pk)

ALTER TABLE emp2 MODIFY COLUMN id INT PRIMARY KEY;
ALTER TABLE emp2 ADD CONSTRAINT my_emp_id_pk PRIMARY KEY(id);

#2.向表dept2的id列中添加PRIMARY KEY約束(my_dept_id_pk)

#3.向表emp2中添加列dept_id,並在其中定義FOREIGN KEY約束,與之相關聯的列是dept2表中的id列。
ALTER TABLE emp2 ADD COLUMN dept_id INT;
ALTER TABLE emp2 ADD CONSTRAINT fk_emp2_dept2 FOREIGN KEY(dept_id) REFERENCES dept2(id);

		位置		支持的約束類型			是否可以起約束名
列級約束:	列的後面	語法都支持,但外鍵沒有效果	不可以
表級約束:	所有列的下面	默認和非空不支持,其他支持	可以(主鍵沒有效果)

#=================================================================================
#標識列
/*
又稱爲自增長列
含義:可以不用手動的插入值,系統提供默認的序列值


特點:
1、標識列必須和主鍵搭配嗎?不一定,但要求是一個key
2、一個表可以有幾個標識列?至多一個!
3、標識列的類型只能是數值型
4、標識列可以通過 SET auto_increment_increment=3;設置步長
可以通過 手動插入值,設置起始值


*/

#一、創建表時設置標識列


DROP TABLE IF EXISTS tab_identity;
CREATE TABLE tab_identity(
	id INT PRIMARY KEY AUTO_INCREMENT ,
	NAME VARCHAR(20)
	


);
INSERT INTO tab_identity(id,NAME) VALUES(NULL,'john');
INSERT INTO tab_identity(NAME) VALUES('lucy');
SELECT * FROM tab_identity;

SHOW VARIABLES LIKE '%auto_increment%'; #查看變量


SET auto_increment_increment=3;


TRUNCATE TABLE tab_identity;

SET auto_increment_increment=3;  #設置步長

# 2修改表時設置標識列
ALTER TABLE tab_indentity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;

# 3修改表時刪除標識列
ALTER TABLE tab_indentity MODIFY COLUMN id INT;

SHOW ENGINES;#查看存儲引擎
#=======================================================================================
#TCL
/*
Transaction Control Language 事務控制語言

事務:
一個或一組sql語句組成一個執行單元,這個執行單元要麼全部執行,要麼全部不執行。

案例:轉賬

張三丰  1000
郭襄	1000

update 表 set 張三丰的餘額=500 where name='張三丰'
意外
update 表 set 郭襄的餘額=1500 where name='郭襄'


事務的特性:
ACID
原子性:一個事務不可再分割,要麼都執行要麼都不執行
一致性:一個事務執行會使數據從一個一致狀態切換到另外一個一致狀態
隔離性:一個事務的執行不受其他事務的干擾
持久性:一個事務一旦提交,則會永久的改變數據庫的數據.



事務的創建
分類:SHOW VARIABLES LIKE 'autocommit';
隱式事務:事務沒有明顯的開啓和結束的標記
比如insert、update、delete語句

delete from 表 where id =1;

顯式事務:事務具有明顯的開啓和結束的標記
前提:必須先設置自動提交功能爲禁用

set autocommit=0;

步驟1:開啓事務
set autocommit=0;
start transaction;可選的     開啓事務
步驟2:編寫事務中的sql語句(select insert update delete)
語句1;
語句2;
...

步驟3:結束事務
commit;提交事務
rollback;回滾事務

savepoint 節點名;設置保存點



事務的隔離級別:
		  髒讀		不可重複讀	幻讀
read uncommitted:√		√		√
read committed:  ×		√		√
repeatable read: ×		×		√
serializable	  ×             ×               ×

在黑窗口進行執行語句

mysql中默認 第三個隔離級別 repeatable read
oracle中默認第二個隔離級別 read committed
查看隔離級別
select @@tx_isolation;
設置隔離級別
set session|global transaction isolation level 隔離級別;




開啓事務的語句;
update 表 set 張三丰的餘額=500 where name='張三丰'

update 表 set 郭襄的餘額=1500 where name='郭襄' 
結束事務的語句;



*/

SHOW VARIABLES LIKE 'autocommit';
SHOW ENGINES;

#1.演示事務的使用步驟
創建表    test數據庫
DROP TABLE IF EXISTS account;
CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(20),
	balance DOUBLE
);
INSERT INTO `account`(username,balance)
VALUES('張無忌',1000),('趙敏',1000);
#開啓事務
SET autocommit=0;
START TRANSACTION;
#編寫一組事務的語句
UPDATE account SET balance = 1000 WHERE username='張無忌';
UPDATE account SET balance = 1000 WHERE username='趙敏';

#結束事務
ROLLBACK;
#commit;

SELECT * FROM account;


#2.演示事務對於delete和truncate的處理的區別

#演示delete
SET autocommit=0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;

#演示truncate
SET autocommit=0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;

#==============================================================
#視圖
/*
含義:虛擬表,和普通表一樣使用
mysql5.1版本出現的新特性,是通過表動態生成的數據

     (視圖與普通表的對比)
比如:舞蹈班和普通班級的對比
	創建語法的關鍵字	是否實際佔用物理空間	使用

視圖	create view		只是保存了sql邏輯	增刪改查,只是一般不能增刪改

表	create table		保存了數據		增刪改查


*/

#視圖案例講解
#案例:查詢姓張的學生名和專業名
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.`majorid`= m.`id`
WHERE s.`stuname` LIKE '張%';

CREATE VIEW v1
AS
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.`majorid`= m.`id`;

SELECT * FROM v1 WHERE stuname LIKE '張%';


#一、創建視圖
/*
語法:
create view 視圖名
as
查詢語句;

*/
USE myemployees;

#1.查詢姓名中包含a字符的員工名、部門名和工種信息
#①創建
CREATE VIEW myv1
AS

SELECT last_name,department_name,job_title
FROM employees e
JOIN departments d ON e.department_id  = d.department_id
JOIN jobs j ON j.job_id  = e.job_id;

#②使用
SELECT * FROM myv1 WHERE last_name LIKE '%a%';

#2.查詢各部門的平均工資級別

#①創建視圖查看每個部門的平均工資
CREATE VIEW myv2
AS
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

#②使用
SELECT myv2.`ag`,g.grade_level
FROM myv2
JOIN job_grades g
ON myv2.`ag` BETWEEN g.`lowest_sal` AND g.`highest_sal`;

#3.查詢平均工資最低的部門信息

SELECT * FROM myv2 ORDER BY ag LIMIT 1;

#4.查詢平均工資最低的部門名和工資

CREATE VIEW myv3
AS
SELECT * FROM myv2 ORDER BY ag LIMIT 1;


SELECT d.*,m.ag
FROM myv3 m
JOIN departments d
ON m.`department_id`=d.`department_id`;




#二、視圖的修改

#方式一:
/*
create or replace view  視圖名
as
查詢語句;

*/
SELECT * FROM myv3 

CREATE OR REPLACE VIEW myv3
AS
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

#方式二:
/*
語法:
alter view 視圖名
as 
查詢語句;

*/
ALTER VIEW myv3
AS
SELECT * FROM employees;

#三、刪除視圖

/*

語法:drop view 視圖名,視圖名,...;
*/

DROP VIEW emp_v1,emp_v2,myv3;


#四、查看視圖

DESC myv3;

SHOW CREATE VIEW myv3;

#試圖案例講解
#一、創建視圖emp_v1,要求查詢電話號碼以‘011’開頭的員工姓名和工資、郵箱

CREATE OR REPLACE VIEW emp_v1
AS
SELECT last_name,salary,email
FROM employees
WHERE phone_number LIKE '011%';

#二、創建視圖emp_v2,要求查詢部門的最高工資高於12000的部門信息

CREATE OR REPLACE VIEW emp_v2
AS
SELECT MAX(salary) mx_dep,department_id
FROM employees
GROUP BY department_id
HAVING MAX(salary)>12000;


SELECT d.*,m.mx_dep
FROM departments d
JOIN emp_v2 m
ON m.department_id = d.`department_id`;



#五、視圖的更新

CREATE OR REPLACE VIEW myv1
AS
SELECT last_name,email,salary*12*(1+IFNULL(commission_pct,0)) "annual salary"
FROM employees;

CREATE OR REPLACE VIEW myv1
AS
SELECT last_name,email
FROM employees;


SELECT * FROM myv1;
SELECT * FROM employees;
#1.插入

INSERT INTO myv1 VALUES('張飛','[email protected]');

#2.修改
UPDATE myv1 SET last_name = '張無忌' WHERE last_name='張飛';

#3.刪除
DELETE FROM myv1 WHERE last_name = '張無忌';

#具備以下特點的視圖不允許更新


#①包含以下關鍵字的sql語句:分組函數、distinct、group  by、having、union或者union all

CREATE OR REPLACE VIEW myv1
AS
SELECT MAX(salary) m,department_id
FROM employees
GROUP BY department_id;

SELECT * FROM myv1;

#更新
UPDATE myv1 SET m=9000 WHERE department_id=10;

#②常量視圖
CREATE OR REPLACE VIEW myv2
AS

SELECT 'john' NAME;

SELECT * FROM myv2;

#更新
UPDATE myv2 SET NAME='lucy';





#③Select中包含子查詢

CREATE OR REPLACE VIEW myv3
AS

SELECT department_id,(SELECT MAX(salary) FROM employees) 最高工資
FROM departments;

#更新
SELECT * FROM myv3;
UPDATE myv3 SET 最高工資=100000;


#④join
CREATE OR REPLACE VIEW myv4
AS

SELECT last_name,department_name
FROM employees e
JOIN departments d
ON e.department_id  = d.department_id;

#更新

SELECT * FROM myv4;

UPDATE myv4 SET last_name  = '張飛' WHERE last_name='Whalen';

INSERT INTO myv4 VALUES('陳真','xxxx');



#⑤from一個不能更新的視圖
CREATE OR REPLACE VIEW myv5
AS

SELECT * FROM myv3;

#更新

SELECT * FROM myv5;

UPDATE myv5 SET 最高工資=10000 WHERE department_id=60;



#⑥where子句的子查詢引用了from子句中的表

CREATE OR REPLACE VIEW myv6
AS

SELECT last_name,email,salary
FROM employees
WHERE employee_id IN(
	SELECT  manager_id
	FROM employees
	WHERE manager_id IS NOT NULL
);

#更新
SELECT * FROM myv6;
UPDATE myv6 SET salary=10000 WHERE last_name = 'k_ing';


#3.演示savepoint 的使用
SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=1;
SAVEPOINT a;#設置保存點
DELETE FROM account WHERE id=2;
ROLLBACK TO a;#回滾到保存點


SELECT * FROM account;

#===================================================================
#基礎測試講解
/*

#1,創建表Book表,字段如下
bid 整型,要求主鍵
bname 字符型,要求設置唯一鍵,並非空
price 浮點型·,要求由默認值 10
btypeId 類型編號,要求引用bookType表的id字段

create table Book(
bid int primary key,
bname varchar(20) unique not null,
price float default 10,
btypeId int,
foreign key(btypeid) reference bookType(id)
);

已知bookType表(不用創建),字段如下
id 
name


*/
# 2 開啓事務  向表中插入一條數據
SET autocommit = 0;
INSERT INTO book(bid,bname,price,btypeId)
VALUES(1,'小李飛刀',100,1);

ROLLBACK;

# 3 創建視圖,實現查詢價格大於100的書名和類型
CREATE VIEW myv1
AS
SELECT bname,NAME
FROM book b 
JOIN bookType t ON b.btypeid = t.id
WHERE price > 100;

# 4 修改試圖,實現查詢價格在90-120之間的書名和價格
CREATE OR REPLACE VIEW myv1
AS
SELECT bname,price
FROM book
WHERE price BETWEEN 90 AND 120;

# 5 刪除剛纔創建的視圖
DROP VIEW myv1;

#=========================================================
#變量
/*
系統變量:
	全局變量
	會話變量

自定義變量:
	用戶變量
	局部變量

*/
#一、系統變量
/*
說明:變量由系統定義,不是用戶定義,屬於服務器層面
注意:全局變量需要添加global關鍵字,會話變量需要添加session關鍵字,如果不寫,默認會話級別
使用步驟:
1、查看所有系統變量
show global|【session】variables;
2、查看滿足條件的部分系統變量
show global|【session】 variables like '%char%';
3、查看指定的系統變量的值
select @@global|【session】系統變量名;
4、爲某個系統變量賦值
方式一:
set global|【session】系統變量名=值;
方式二:
set @@global|【session】系統變量名=值;

*/
#1》全局變量
/*
作用域:針對於所有會話(連接)有效,但不能跨重啓
*/
#①查看所有全局變量
SHOW GLOBAL VARIABLES;
#②查看滿足條件的部分系統變量
SHOW GLOBAL VARIABLES LIKE '%char%';
#③查看指定的系統變量的值
SELECT @@global.autocommit;
#④爲某個系統變量賦值
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;

#2》會話變量
/*
作用域:針對於當前會話(連接)有效
*/
#①查看所有會話變量
SHOW SESSION VARIABLES;
#②查看滿足條件的部分會話變量
SHOW SESSION VARIABLES LIKE '%char%';
#③查看指定的會話變量的值
SELECT @@autocommit;
SELECT @@session.tx_isolation;
#④爲某個會話變量賦值
SET @@session.tx_isolation='read-uncommitted';
SET SESSION tx_isolation='read-committed';

#二、自定義變量
/*
說明:變量由用戶自定義,而不是系統提供的
使用步驟:
1、聲明
2、賦值
3、使用(查看、比較、運算等)
*/

#1》用戶變量
/*
作用域:針對於當前會話(連接)有效,同於會話變量的作用域
*/

#賦值操作符:=或:=
#①聲明並初始化
SET @變量名=值;
SET @變量名:=值;
SELECT @變量名:=值;

#②賦值(更新變量的值)
#方式一:
	SET @變量名=值;
	SET @變量名:=值;
	SELECT @變量名:=值;
#方式二:通過 select into 
	SELECT 字段 INTO @變量名
	FROM 表;
#③使用(查看變量的值)
SELECT @變量名;


#2》局部變量
/*
作用域:僅僅在定義它的begin end塊中有效
應用在 begin end中的第一句話
*/

#①聲明
DECLARE 變量名 類型;
DECLARE 變量名 類型 【DEFAULT 值】;


#②賦值(更新變量的值)

#方式一:
	SET 局部變量名=值;
	SET 局部變量名:=值;
	SELECT 局部變量名:=值;
#方式二:
	SELECT 字段 INTO 具備變量名
	FROM 表;
#③使用(查看變量的值)
SELECT 局部變量名;


#案例:聲明兩個變量,求和並打印

#用戶變量
SET @m=1;
SET @n=1;
SET @sum=@m+@n;
SELECT @sum;

#局部變量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 1;
DECLARE SUM INT;
SET SUM=m+n;
SELECT SUM;


#用戶變量和局部變量的對比

		作用域			定義位置		語法
用戶變量	當前會話		會話的任何地方		加@符號,不用指定類型
局部變量	定義它的BEGIN END中 	BEGIN END的第一句話	一般不用加@,需要指定類型


##==========================================================================================
#存儲過程和函數
/*
存儲過程和函數:類似於java中的方法
好處:
1、提高代碼的重用性
2、簡化操作



*/
#存儲過程
/*
含義:一組預先編譯好的SQL語句的集合,理解成批處理語句
1、提高代碼的重用性
2、簡化操作
3、減少了編譯次數並且減少了和數據庫服務器的連接次數,提高了效率
*/

#一、創建語法

CREATE PROCEDURE 存儲過程名(參數列表)
BEGIN

	存儲過程體(一組合法的SQL語句)
END

#注意:
/*
1、參數列表包含三部分
參數模式  參數名  參數類型
舉例:
in stuname varchar(20)

參數模式:
in:該參數可以作爲輸入,也就是該參數需要調用方傳入值
out:該參數可以作爲輸出,也就是該參數可以作爲返回值
inout:該參數既可以作爲輸入又可以作爲輸出,也就是該參數既需要傳入值,又可以返回值

2、如果存儲過程體僅僅只有一句話,begin end可以省略
存儲過程體中的每條sql語句的結尾要求必須加分號。
存儲過程的結尾可以使用 delimiter 重新設置
語法:
delimiter 結束標記
案例:
delimiter $
*/


#二、調用語法

CALL 存儲過程名(實參列表);

#--------------------------------案例演示-----------------------------------
#1.空參列表
#案例:插入到admin表中五條記錄

SELECT * FROM admin;


/*要在黑窗口中執行纔可以
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
	INSERT INTO admin(username,`password`) 
	VALUES('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom','0000');
END $


#調用
CALL myp1()$
select * from admin;
*/
#2.創建帶in模式參數的存儲過程

#案例1:創建存儲過程實現 根據女神名,查詢對應的男神信息

/*要在黑窗口中執行纔可以
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
	SELECT bo.*
	FROM boys bo
	RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
	WHERE b.name=beautyName;
	

END $

#調用
CALL myp2('柳巖')$

*/
#案例2 :創建存儲過程實現,用戶是否登錄成功

/*要在黑窗口中執行纔可以
CREATE PROCEDURE myp4(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
	DECLARE result INT DEFAULT 0;#聲明並初始化
	
	SELECT COUNT(*) INTO result#賦值
	FROM admin
	WHERE admin.username = username
	AND admin.password = PASSWORD;
	
	SELECT IF(result>0,'成功','失敗');#使用
END $

#調用
CALL myp3('張飛','8888')$
*/

#3.創建out 模式參數的存儲過程
#案例1:根據輸入的女神名,返回對應的男神名

CREATE PROCEDURE myp5(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
	SELECT bo.boyname INTO boyname  #  INTO boyname  賦值給boyname
	FROM boys bo
	RIGHT JOIN
	beauty b ON b.boyfriend_id = bo.id
	WHERE b.name=beautyName ;
	
END $

#調用
SET @bName$
CALL myp5('小昭',@bName)$

SELECT @bName$;

#案例2:根據輸入的女神名,返回對應的男神名和魅力值

CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT userCP INT) 
BEGIN
	SELECT bo.boyName ,bo.userCP INTO boyName,userCP
	FROM boys bo
	INNER JOIN beauty b ON bo.id = b.boyfriend_id
	WHERE b.name=beautyName ;
	
END $


#調用
CALL myp6('小昭',@bName,@usercp)$
SELECT @bName,@usercp



#4.創建帶inout模式參數的存儲過程
#案例1:傳入a和b兩個值,最終a和b都翻倍並返回

CREATE PROCEDURE myp8(INOUT a INT ,INOUT b INT)
BEGIN
	SET a=a*2;
	SET b=b*2;
END $

#調用
SET @m=10$
SET @n=20$
CALL myp8(@m,@n)$
SELECT @m,@n$


#三、刪除存儲過程
#語法:drop procedure 存儲過程名
DROP PROCEDURE p1;
DROP PROCEDURE p2,p3;#×

#四、查看存儲過程的信息
DESC myp2;×
SHOW CREATE PROCEDURE  myp2;


#存儲過程案例講解
#一、創建存儲過程實現傳入用戶名和密碼,插入到admin表中

CREATE PROCEDURE test_pro1(IN username VARCHAR(20),IN loginPwd VARCHAR(20))
BEGIN
	INSERT INTO admin(admin.username,PASSWORD)
	VALUES(username,loginpwd);
END $

CALL test_pro1('admin','0000');

#二、創建存儲過程實現傳入女神編號,返回女神名稱和女神電話

CREATE PROCEDURE test_pro2(IN id INT,OUT NAME VARCHAR(20),OUT phone VARCHAR(20))

BEGIN
	SELECT b.name ,b.phone INTO NAME,phone     #查詢的兩個值b.name ,b.phone賦值給NAME,phone 
	FROM beauty b
	WHERE b.id = id;

END $

CALL test_pro2(1,'@n','@p');
SELECT @n,@p;
#三、創建存儲存儲過程或函數實現傳入兩個女神生日,返回大小

CREATE PROCEDURE test_pro3(IN birth1 DATETIME,IN birth2 DATETIME,OUT result INT)
BEGIN
	SELECT DATEDIFF(birth1,birth2) INTO result;
END $

CALL test_pro3('1998-1-1',NOW(),@result)$
SELECT @result;

#四、創建存儲過程或函數實現傳入一個日期,格式化成xx年xx月xx日並返回
CREATE PROCEDURE test_pro4(IN mydate DATETIME,OUT strDate VARCHAR(50))
BEGIN
	SELECT DATE_FORMAT(mydate,'%y年%m月%d日') INTO strDate;
END $

CALL test_pro4(NOW(),@str)$
SELECT @str $

#五、創建存儲過程或函數實現傳入女神名稱,返回:女神 and 男神  格式的字符串
如 傳入 :小昭
返回: 小昭 AND 張無忌
DROP PROCEDURE test_pro5 $
CREATE PROCEDURE test_pro5(IN beautyName VARCHAR(20),OUT str VARCHAR(50))
BEGIN
	SELECT CONCAT(beautyName,' and ',IFNULL(boyName,'null')) INTO str
	FROM boys bo
	RIGHT JOIN beauty b ON b.boyfriend_id = bo.id
	WHERE b.name=beautyName;
	
END $

CALL test_pro5('柳巖',@str)$
SELECT @str $



#六、創建存儲過程或函數,根據傳入的條目數和起始索引,查詢beauty表的記錄
DROP PROCEDURE test_pro6$
CREATE PROCEDURE test_pro6(IN startIndex INT,IN size INT)
BEGIN
	SELECT * FROM beauty LIMIT startIndex,size;
END $

CALL test_pro6(3,5)$

#======================================================================
#函數
/*
含義:一組預先編譯好的SQL語句的集合,理解成批處理語句
1、提高代碼的重用性
2、簡化操作
3、減少了編譯次數並且減少了和數據庫服務器的連接次數,提高了效率

區別:

存儲過程:可以有0個返回,也可以有多個返回,適合做批量插入、批量更新
函數:有且僅有1 個返回,適合做處理數據後返回一個結果

*/

#一、創建語法
CREATE FUNCTION 函數名(參數列表) RETURNS 返回類型
BEGIN
	函數體
END
/*

注意:
1.參數列表 包含兩部分:
參數名 參數類型

2.函數體:肯定會有return語句,如果沒有會報錯
如果return語句沒有放在函數體的最後也不報錯,但不建議

return 值;
3.函數體中僅有一句話,則可以省略begin end
4.使用 delimiter語句設置結束標記

*/

#二、調用語法
SELECT 函數名(參數列表)


#------------------------------案例演示----------------------------
#1.無參有返回
#案例:返回公司的員工個數
CREATE FUNCTION myf1() RETURNS INT
BEGIN

	DECLARE c INT DEFAULT 0;#定義局部變量
	SELECT COUNT(*) INTO c#賦值
	FROM employees;
	RETURN c;
	
END $

SELECT myf1()$


#2.有參有返回
#案例1:根據員工名,返回它的工資

CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN
	SET @sal=0;#定義用戶變量 
	SELECT salary INTO @sal   #賦值
	FROM employees
	WHERE last_name = empName;
	
	RETURN @sal;
END $

SELECT myf2('k_ing') $

#案例2:根據部門名,返回該部門的平均工資

CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
	DECLARE sal DOUBLE ;
	SELECT AVG(salary) INTO sal
	FROM employees e
	JOIN departments d ON e.department_id = d.department_id
	WHERE d.department_name=deptName;
	RETURN sal;
END $

SELECT myf3('IT')$

#三、查看函數

SHOW CREATE FUNCTION myf3;

#四、刪除函數
DROP FUNCTION myf3;

#案例
#一、創建函數,實現傳入兩個float,返回二者之和

CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
	DECLARE SUM FLOAT DEFAULT 0;
	SET SUM=num1+num2;
	RETURN SUM;
END $

SELECT test_fun1(1,2)$

#======================================================================
#流程控制結構
/*
順序結構:程序從上往下依次執行
分支結構:程序從兩條或多條路徑中選擇一條去執行
循環結構:程序在滿足一定條件的基礎上,重複執行一段代碼

*/

#一、分支結構
#1.if函數
/*
語法:if(條件,值1,值2)
功能:實現雙分支
應用在begin end中或外面

*/

#2.case結構
/*
語法:
情況1:類似於switch
case 變量或表達式
when 值1 then 語句1;
when 值2 then 語句2;
...
else 語句n;
end 

情況2:
case 
when 條件1 then 語句1;
when 條件2 then 語句2;
...
else 語句n;
end 

應用在begin end 中或外面


*/

#3.if結構

/*
語法:
if 條件1 then 語句1;
elseif 條件2 then 語句2;
....
else 語句n;
end if;
功能:類似於多重if

只能應用在begin end 中

*/

#案例1:創建函數,實現傳入成績,如果成績>90,返回A,如果成績>80,返回B,如果成績>60,返回C,否則返回D

CREATE FUNCTION test_if(score FLOAT) RETURNS CHAR
BEGIN
	DECLARE ch CHAR DEFAULT 'A';
	IF score>90 THEN SET ch='A';
	ELSEIF score>80 THEN SET ch='B';
	ELSEIF score>60 THEN SET ch='C';
	ELSE SET ch='D';
	END IF;
	RETURN ch;
	
	
END $

SELECT test_if(87)$

#案例2:創建存儲過程,如果工資<2000,則刪除,如果5000>工資>2000,則漲工資1000,否則漲工資500


CREATE PROCEDURE test_if_pro(IN sal DOUBLE)
BEGIN
	IF sal<2000 THEN DELETE FROM employees WHERE employees.salary=sal;
	ELSEIF sal>=2000 AND sal<5000 THEN UPDATE employees SET salary=salary+1000 WHERE employees.`salary`=sal;
	ELSE UPDATE employees SET salary=salary+500 WHERE employees.`salary`=sal;
	END IF;
	
END $

CALL test_if_pro(2100)$

#案例1:創建函數,實現傳入成績,如果成績>90,返回A,如果成績>80,返回B,如果成績>60,返回C,否則返回D

CREATE FUNCTION test_case(score FLOAT) RETURNS CHAR
BEGIN 
	DECLARE ch CHAR DEFAULT 'A';
	
	CASE 
	WHEN score>90 THEN SET ch='A';
	WHEN score>80 THEN SET ch='B';
	WHEN score>60 THEN SET ch='C';
	ELSE SET ch='D';
	END CASE;
	
	RETURN ch;
END $

SELECT test_case(56)$



#二、循環結構
/*
分類:
while、loop、repeat

循環控制:

iterate類似於 continue,繼續,結束本次循環,繼續下一次
leave 類似於  break,跳出,結束當前所在的循環

*/

#1.while
/*

語法:

【標籤:】while 循環條件 do
	循環體;
end while【 標籤】;

聯想:

while(循環條件){

	循環體;
}

*/

#2.loop
/*

語法:
【標籤:】loop
	循環體;
end loop 【標籤】;

可以用來模擬簡單的死循環



*/

#3.repeat
/*
語法:
【標籤:】repeat
	循環體;
until 結束循環的條件
end repeat 【標籤】;


*/

#1.沒有添加循環控制語句
#案例:批量插入,根據次數插入到admin表中多條記錄
DROP PROCEDURE pro_while1$
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	WHILE i<=insertCount DO
		INSERT INTO admin(username,`password`) VALUES(CONCAT('Rose',i),'666');
		SET i=i+1;
	END WHILE;
	
END $

CALL pro_while1(100)$


/*

int i=1;
while(i<=insertcount){

	//插入
	
	i++;

}

*/


#2.添加leave語句

#案例:批量插入,根據次數插入到admin表中多條記錄,如果次數>20則停止
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	a:WHILE i<=insertCount DO
		INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000');
		IF i>=20 THEN LEAVE a;
		END IF;
		SET i=i+1;
	END WHILE a;
END $


CALL test_while1(100)$


#3.添加iterate語句

#案例:批量插入,根據次數插入到admin表中多條記錄,只插入偶數次
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i<=insertCount DO
		SET i=i+1;
		IF MOD(i,2)!=0 THEN ITERATE a;
		END IF;
		
		INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000');
		
	END WHILE a;
END $


CALL test_while1(100)$

/*

int i=0;
while(i<=insertCount){
	i++;
	if(i%2==0){
		continue;
	}
	插入
	
}

*/

###流程控制經典案例

/*一、已知表stringcontent
其中字段:
id 自增長
content varchar(20)

向該表插入指定個數的,隨機的字符串
*/
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
	id INT PRIMARY KEY AUTO_INCREMENT,
	content VARCHAR(20)
	
);

DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
	DECLARE startIndex INT;#代表初始索引
	DECLARE len INT;#代表截取的字符長度
	WHILE i<=insertcount DO
		SET startIndex=FLOOR(RAND()*26+1);#代表初始索引,隨機範圍1-26
		SET len=FLOOR(RAND()*(20-startIndex+1)+1);#代表截取長度,隨機範圍1-(20-startIndex+1)
		INSERT INTO stringcontent(content) VALUES(SUBSTR(str,startIndex,len));
		SET i=i+1;
	END WHILE;

END $

CALL test_randstr_insert(10)$

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章