1. 程式人生 > 實用技巧 >SQL多表&事務

SQL多表&事務

	1. 多表查詢

	2. 事務

	3. DCL


## 多表查詢:
	* 查詢語法:
		select
			列名列表
		from
			表名列表
		where....
	* 準備sql
		# 建立部門表
		CREATE TABLE dept(
			id INT PRIMARY KEY AUTO_INCREMENT,
			NAME VARCHAR(20)
		);
		INSERT INTO dept (NAME) VALUES ('開發部'),('市場部'),('財務部');
		# 建立員工表
		CREATE TABLE emp (
			id INT PRIMARY KEY AUTO_INCREMENT,
			NAME VARCHAR(10),
			gender CHAR(1), -- 性別
			salary DOUBLE, -- 工資
			join_date DATE, -- 入職日期
			dept_id INT,
			FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外來鍵,關聯部門表(部門表的主鍵)
		);
		INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孫悟空','男',7200,'2013-02-24',1);
		INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('豬八戒','男',3600,'2010-12-02',2);
		INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
		INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
		INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
	* 笛卡爾積:
		* 有兩個集合A,B .取這兩個集合的所有組成情況。
		* 要完成多表查詢,需要消除無用的資料
	* 多表查詢的分類:
		1. 內連線查詢:
			1. 隱式內連線:使用where條件消除無用資料
				* 例子:
				-- 查詢所有員工資訊和對應的部門資訊

				SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
				
				-- 查詢員工表的名稱,性別。部門表的名稱
				SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
				
				SELECT 
					t1.name, -- 員工表的姓名
					t1.gender,-- 員工表的性別
					t2.name -- 部門表的名稱
				FROM
					emp t1,
					dept t2
				WHERE 
					t1.`dept_id` = t2.`id`;

	
			2. 顯式內連線:
				* 語法: select 欄位列表 from 表名1 [inner] join 表名2 on 條件
				* 例如:
					* SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;	
					* SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`;	

			3. 內連線查詢:
				1. 從哪些表中查詢資料
				2. 條件是什麼
				3. 查詢哪些欄位
		2. 外連結查詢:
			1. 左外連線:
				* 語法:select 欄位列表 from 表1 left [outer] join 表2 on 條件;
				* 查詢的是左表所有資料以及其交集部分。
				* 例子:
					-- 查詢所有員工資訊,如果員工有部門,則查詢部門名稱,沒有部門,則不顯示部門名稱
					SELECT 	t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
			2. 右外連線:
				* 語法:select 欄位列表 from 表1 right [outer] join 表2 on 條件;
				* 查詢的是右表所有資料以及其交集部分。
				* 例子:
					SELECT 	* FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
		3. 子查詢:
			* 概念:查詢中巢狀查詢,稱巢狀查詢為子查詢。
				-- 查詢工資最高的員工資訊
				-- 1 查詢最高的工資是多少 9000
				SELECT MAX(salary) FROM emp;
				
				-- 2 查詢員工資訊,並且工資等於9000的
				SELECT * FROM emp WHERE emp.`salary` = 9000;
				
				-- 一條sql就完成這個操作。子查詢
				SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);

			* 子查詢不同情況
				1. 子查詢的結果是單行單列的:
					* 子查詢可以作為條件,使用運算子去判斷。 運算子: > >= < <= =
					* 
					-- 查詢員工工資小於平均工資的人
					SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
				2. 子查詢的結果是多行單列的:
					* 子查詢可以作為條件,使用運算子in來判斷
					-- 查詢'財務部'和'市場部'所有的員工資訊
					SELECT id FROM dept WHERE NAME = '財務部' OR NAME = '市場部';
					SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
					-- 子查詢
					SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '財務部' OR NAME = '市場部');

				3. 子查詢的結果是多行多列的:
					* 子查詢可以作為一張虛擬表參與查詢
					-- 查詢員工入職日期是2011-11-11日之後的員工資訊和部門資訊
					-- 子查詢
					SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
					WHERE t1.id = t2.dept_id;
					
					-- 普通內連線
					SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` >  '2011-11-11'

		* 多表查詢練習

				-- 部門表
				CREATE TABLE dept (
				  id INT PRIMARY KEY PRIMARY KEY, -- 部門id
				  dname VARCHAR(50), -- 部門名稱
				  loc VARCHAR(50) -- 部門所在地
				);
				
				-- 新增4個部門
				INSERT INTO dept(id,dname,loc) VALUES 
				(10,'教研部','北京'),
				(20,'學工部','上海'),
				(30,'銷售部','廣州'),
				(40,'財務部','深圳');
				
				
				
				-- 職務表,職務名稱,職務描述
				CREATE TABLE job (
				  id INT PRIMARY KEY,
				  jname VARCHAR(20),
				  description VARCHAR(50)
				);
				
				-- 新增4個職務
				INSERT INTO job (id, jname, description) VALUES
				(1, '董事長', '管理整個公司,接單'),
				(2, '經理', '管理部門員工'),
				(3, '銷售員', '向客人推銷產品'),
				(4, '文員', '使用辦公軟體');
				
				
				
				-- 員工表
				CREATE TABLE emp (
				  id INT PRIMARY KEY, -- 員工id
				  ename VARCHAR(50), -- 員工姓名
				  job_id INT, -- 職務id
				  mgr INT , -- 上級領導
				  joindate DATE, -- 入職日期
				  salary DECIMAL(7,2), -- 工資
				  bonus DECIMAL(7,2), -- 獎金
				  dept_id INT, -- 所在部門編號
				  CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
				  CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
				);
				
				-- 新增員工
				INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
				(1001,'孫悟空',4,1004,'2000-12-17','8000.00',NULL,20),
				(1002,'盧俊義',3,1006,'2001-02-20','16000.00','3000.00',30),
				(1003,'林沖',3,1006,'2001-02-22','12500.00','5000.00',30),
				(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
				(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
				(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
				(1007,'劉備',2,1009,'2001-09-01','24500.00',NULL,10),
				(1008,'豬八戒',4,1004,'2007-04-19','30000.00',NULL,20),
				(1009,'羅貫中',1,NULL,'2001-11-17','50000.00',NULL,10),
				(1010,'吳用',3,1006,'2001-09-08','15000.00','0.00',30),
				(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
				(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
				(1013,'小白龍',4,1004,'2001-12-03','30000.00',NULL,20),
				(1014,'關羽',4,1007,'2002-01-23','13000.00',NULL,10);
				
				
				
				-- 工資等級表
				CREATE TABLE salarygrade (
				  grade INT PRIMARY KEY,   -- 級別
				  losalary INT,  -- 最低工資
				  hisalary INT -- 最高工資
				);
				
				-- 新增5個工資等級
				INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
				(1,7000,12000),
				(2,12010,14000),
				(3,14010,20000),
				(4,20010,30000),
				(5,30010,99990);
				
				-- 需求:
				
				-- 1.查詢所有員工資訊。查詢員工編號,員工姓名,工資,職務名稱,職務描述
				/*
					分析:
						1.員工編號,員工姓名,工資,需要查詢emp表  職務名稱,職務描述 需要查詢job表
						2.查詢條件 emp.job_id = job.id
				
				*/
				SELECT 
					t1.`id`, -- 員工編號
					t1.`ename`, -- 員工姓名
					t1.`salary`,-- 工資
					t2.`jname`, -- 職務名稱
					t2.`description` -- 職務描述
				FROM 
					emp t1, job t2
				WHERE 
					t1.`job_id` = t2.`id`;
				
				
				
				-- 2.查詢員工編號,員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置
				/*
					分析:
						1. 員工編號,員工姓名,工資 emp  職務名稱,職務描述 job  部門名稱,部門位置 dept
						2. 條件: emp.job_id = job.id and emp.dept_id = dept.id
				*/
				
				SELECT 
					t1.`id`, -- 員工編號
					t1.`ename`, -- 員工姓名
					t1.`salary`,-- 工資
					t2.`jname`, -- 職務名稱
					t2.`description`, -- 職務描述
					t3.`dname`, -- 部門名稱
					t3.`loc` -- 部門位置
				FROM 
					emp t1, job t2,dept t3
				WHERE 
					t1.`job_id` = t2.`id` AND t1.`dept_id` = t3.`id`;
				   
				-- 3.查詢員工姓名,工資,工資等級
				/*
					分析:
						1.員工姓名,工資 emp  工資等級 salarygrade
						2.條件 emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary
							emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
				*/
				SELECT 
					t1.ename ,
					t1.`salary`,
					t2.*
				FROM emp t1, salarygrade t2
				WHERE t1.`salary` BETWEEN t2.`losalary` AND t2.`hisalary`;
				
				
				
				-- 4.查詢員工姓名,工資,職務名稱,職務描述,部門名稱,部門位置,工資等級
				/*
					分析:
						1. 員工姓名,工資 emp , 職務名稱,職務描述 job 部門名稱,部門位置,dept  工資等級 salarygrade
						2. 條件: emp.job_id = job.id and emp.dept_id = dept.id and emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
							
				*/
				SELECT 
					t1.`ename`,
					t1.`salary`,
					t2.`jname`,
					t2.`description`,
					t3.`dname`,
					t3.`loc`,
					t4.`grade`
				FROM 
					emp t1,job t2,dept t3,salarygrade t4
				WHERE 
					t1.`job_id` = t2.`id` 
					AND t1.`dept_id` = t3.`id`
					AND t1.`salary` BETWEEN t4.`losalary` AND t4.`hisalary`;
				
				
				
				-- 5.查詢出部門編號、部門名稱、部門位置、部門人數
				
				/*
					分析:
						1.部門編號、部門名稱、部門位置 dept 表。 部門人數 emp表
						2.使用分組查詢。按照emp.dept_id完成分組,查詢count(id)
						3.使用子查詢將第2步的查詢結果和dept表進行關聯查詢
						
				*/
				SELECT 
					t1.`id`,t1.`dname`,t1.`loc` , t2.total
				FROM 
					dept t1,
					(SELECT
						dept_id,COUNT(id) total
					FROM 
						emp
					GROUP BY dept_id) t2
				WHERE t1.`id` = t2.dept_id;
				
				
				-- 6.查詢所有員工的姓名及其直接上級的姓名,沒有領導的員工也需要查詢
				
				/*
					分析:
						1.姓名 emp, 直接上級的姓名 emp
							* emp表的id 和 mgr 是自關聯
						2.條件 emp.id = emp.mgr
						3.查詢左表的所有資料,和 交集資料
							* 使用左外連線查詢
					
				*/
				/*
				select
					t1.ename,
					t1.mgr,
					t2.`id`,
					t2.ename
				from emp t1, emp t2
				where t1.mgr = t2.`id`;
				
				*/
				
				SELECT 
					t1.ename,
					t1.mgr,
					t2.`id`,
					t2.`ename`
				FROM emp t1
				LEFT JOIN emp t2
				ON t1.`mgr` = t2.`id`;


## 事務

	1. 事務的基本介紹
		1. 概念:
			*  如果一個包含多個步驟的業務操作,被事務管理,那麼這些操作要麼同時成功,要麼同時失敗。
			
		2. 操作:
			1. 開啟事務: start transaction;
			2. 回滾:rollback;
			3. 提交:commit;
		3. 例子:
			CREATE TABLE account (
				id INT PRIMARY KEY AUTO_INCREMENT,
				NAME VARCHAR(10),
				balance DOUBLE
			);
			-- 新增資料
			INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);
			
			
			SELECT * FROM account;
			UPDATE account SET balance = 1000;
			-- 張三給李四轉賬 500 元
			
			-- 0. 開啟事務
			START TRANSACTION;
			-- 1. 張三賬戶 -500
			
			UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
			-- 2. 李四賬戶 +500
			-- 出錯了...
			UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
			
			-- 發現執行沒有問題,提交事務
			COMMIT;
			
			-- 發現出問題了,回滾事務
			ROLLBACK;
		4. MySQL資料庫中事務預設自動提交
			
			* 事務提交的兩種方式:
				* 自動提交:
					* mysql就是自動提交的
					* 一條DML(增刪改)語句會自動提交一次事務。
				* 手動提交:
					* Oracle 資料庫預設是手動提交事務
					* 需要先開啟事務,再提交
			* 修改事務的預設提交方式:
				* 檢視事務的預設提交方式:SELECT @@autocommit; -- 1 代表自動提交  0 代表手動提交
				* 修改預設提交方式: set @@autocommit = 0;


	2. 事務的四大特徵:
		1. 原子性:是不可分割的最小操作單位,要麼同時成功,要麼同時失敗。
		2. 永續性:當事務提交或回滾後,資料庫會持久化的儲存資料。
		3. 隔離性:多個事務之間。相互獨立。
		4. 一致性:事務操作前後,資料總量不變
	3. 事務的隔離級別(瞭解)
		* 概念:多個事務之間隔離的,相互獨立的。但是如果多個事務操作同一批資料,則會引發一些問題,設定不同的隔離級別就可以解決這些問題。
		* 存在問題:
			1. 髒讀:一個事務,讀取到另一個事務中沒有提交的資料
			2. 不可重複讀(虛讀):在同一個事務中,兩次讀取到的資料不一樣。
			3. 幻讀:一個事務操作(DML)資料表中所有記錄,另一個事務添加了一條資料,則第一個事務查詢不到自己的修改。
		* 隔離級別:
			1. read uncommitted:讀未提交
				* 產生的問題:髒讀、不可重複讀、幻讀
			2. read committed:讀已提交 (Oracle)
				* 產生的問題:不可重複讀、幻讀
			3. repeatable read:可重複讀 (MySQL預設)
				* 產生的問題:幻讀
			4. serializable:序列化
				* 可以解決所有的問題

			* 注意:隔離級別從小到大安全性越來越高,但是效率越來越低
			* 資料庫查詢隔離級別:
				* select @@tx_isolation;
			* 資料庫設定隔離級別:
				* set global transaction isolation level  級別字串;

		* 演示:
			set global transaction isolation level read uncommitted;
			start transaction;
			-- 轉賬操作
			update account set balance = balance - 500 where id = 1;
			update account set balance = balance + 500 where id = 2;



## DCL:
	* SQL分類:
		1. DDL:操作資料庫和表
		2. DML:增刪改表中資料
		3. DQL:查詢表中資料
		4. DCL:管理使用者,授權

	* DBA:資料庫管理員

	* DCL:管理使用者,授權
		1. 管理使用者
			1. 新增使用者:
				* 語法:CREATE USER '使用者名稱'@'主機名' IDENTIFIED BY '密碼';
			2. 刪除使用者:
				* 語法:DROP USER '使用者名稱'@'主機名';
			3. 修改使用者密碼:
				
				UPDATE USER SET PASSWORD = PASSWORD('新密碼') WHERE USER = '使用者名稱';
				UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';
				
				SET PASSWORD FOR '使用者名稱'@'主機名' = PASSWORD('新密碼');
				SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');

				* mysql中忘記了root使用者的密碼?
					1. cmd -- > net stop mysql 停止mysql服務
						* 需要管理員執行該cmd

					2. 使用無驗證方式啟動mysql服務: mysqld --skip-grant-tables
					3. 開啟新的cmd視窗,直接輸入mysql命令,敲回車。就可以登入成功
					4. use mysql;
					5. update user set password = password('你的新密碼') where user = 'root';
					6. 關閉兩個視窗
					7. 開啟工作管理員,手動結束mysqld.exe 的程序
					8. 啟動mysql服務
					9. 使用新密碼登入。
			4. 查詢使用者:
				-- 1. 切換到mysql資料庫
				USE myql;
				-- 2. 查詢user表
				SELECT * FROM USER;
				
				* 萬用字元: % 表示可以在任意主機使用使用者登入資料庫

		2. 許可權管理:
			1. 查詢許可權:
				-- 查詢許可權
				SHOW GRANTS FOR '使用者名稱'@'主機名';
				SHOW GRANTS FOR 'lisi'@'%';

			2. 授予許可權:
				-- 授予許可權
				grant 許可權列表 on 資料庫名.表名 to '使用者名稱'@'主機名';
				-- 給張三使用者授予所有許可權,在任意資料庫任意表上
				
				GRANT ALL ON *.* TO 'zhangsan'@'localhost';
			3. 撤銷許可權:
				-- 撤銷許可權:
				revoke 許可權列表 on 資料庫名.表名 from '使用者名稱'@'主機名';
				REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';