MySQL高階 之 子查詢
>>> 多年前的 SQL 聯絡筆記,現在回頭看很多問題,也懶得整理了,直接搬運到 部落格,僅僅是一些練習題,並非知識庫 <<<
子查詢是巢狀在 SQL 語句中的另一個SELECT 語句
子查詢可以用在很多地方,如 where 後面 from 後面 group by 後面 order by後面、select後面等等
子查詢 (內查詢) 在主查詢執行之前執行
主查詢(外查詢)使用子查詢的結果:
1、問題:查詢工資大於149號員工工資的員工的資訊
SELECT * FROM employees e WHERE salary > ( SELECTsalary FROM employees WHERE employee_id = 149
多列子查詢
主查詢與子查詢返回的多個列進行比較
列比較
多列子查詢中的比較分為兩種:
成對比較
不成對比較
2、問題:查詢與141號或174號員工的manager_id和department_id相同的其他員工的 employee_id, manager_id, department_id
不成對查詢:
SELECT employee_id, manager_id, department_id FROM employees WHERE manager_id IN (SELECT manager_id FROM employees WHERE employee_id IN (141, 174) ) AND department_id IN ( SELECT department_id FROM employees WHERE employee_id IN (141, 174) ) -- 上段查詢的到的是所有的結果(包括了141和174號員工),其他員工則需要排除141和174號員工 AND employee_id NOT IN (141, 174);
成對查詢:
SELECT employee_id, manager_id, department_idFROM employees WHERE (manager_id, department_id) IN ( SELECT manager_id, department_id FROM employees WHERE employee_id IN (141, 174) ) -- 上段查詢的到的是所有的結果(包括了141和174號員工),其他員工則需要排除141和174號員工 AND employee_id NOT IN (141, 174);
在FROM 子句中使用子查詢:
-- 查詢各個部門的平均工資 SELECT AVG(salary) FROM employees GROUP BY department_id;
-- 查詢各個部門的平均工資,顯示部門ID和名稱 SELECT d.department_id, department_name,avg(salary) FROM employees e, departments d WHERE e.department_id = d.department_id GROUP BY e.department_id;
結果如圖所示,生成新的臨時表:
-- 單獨查詢指定部門ID的部門平均工資 SELECT AVG(salary) FROM employees WHERE employees.department_id = 80; -- 或者 SELECT AVG(salary) FROM employees WHERE department_id = 80; -- 或者 SELECT AVG(salary) FROM employees e WHERE e.department_id = 80;
-- 獲取各個部門中平均工資的最大值,Oracle中支援此種方式,主函式巢狀,但是MySQL中不支援 SELECT max(AVG(salary)) FROM employees GROUP BY department_id;
MySQL中不能使用主函式巢狀,解決方案,將上圖查詢結果的臨時表當做一張表再次查詢,
即將 臨時表的查詢語句巢狀如 下一次查詢的 FROM (table)語句中,臨時表也是一張表
示例一:
SELECT temp_id,temp_name,max(avg_sal) FROM ( SELECT d.department_id temp_id, department_name temp_name, avg(salary) avg_sal FROM employees e, departments d WHERE e.department_id = d.department_id GROUP BY e.department_id ) t_avg;
示例二:
SELECT max(avg_sal) FROM ( SELECT AVG(salary) avg_sal -- 臨時表中的列必須有自己的名字 FROM employees GROUP BY department_id ) t_avg; -- 臨時表也必須有自己的名字
注意,臨時表中,每個列都必須有自己的名字,臨時表也必須有自己的名字(推薦使用示例二,只顯示關鍵的列,不容易出錯)
相關子查詢
主查詢與子查詢中有相關性,子查詢無法單獨的完成查詢,子查詢中條件也需要主查詢中提供
問題:返回比 本 部門平均工資高的員工的last_name, department_id, salary及平均工資
-- 方式一:(在 select 和 where 語句中使用子查詢) -- -- 難度分解,先查詢比本部門平均工資高的員工的last_name,department_id, salary SELECT last_name, department_id, salary FROM employees e1 WHERE e1.salary >( SELECT AVG(salary) FROM employees e2 WHERE e2.department_id = e1.department_id GROUP BY department_id ); -- 子查詢需要主查詢提供資料,這就是相關性,相關子查詢
SELECT 語句中加入子查詢 均工資
(SELECT AVG(salary) FROM employees e3 WHERE e3.department_id = e1.department_id GROUP BY e3.department_id)
SELECT last_name, department_id, salary, (SELECT AVG(salary) FROM employees WHERE department_id = e1.department_id GROUP BY department_id) avg_Salary --可以給這個臨時表價格別名,顯示的時候好看一些(也可以不加) FROM employees e1 WHERE e1.salary > ( SELECT AVG(salary) FROM employees e2 WHERE e2.department_id = e1.department_id GROUP BY department_id ); -- 同樣 SELECT 語句中的子查詢關聯條件也用到了主查詢中的資料
結果如下:
方式一:(在 from 語句中使用子查詢),將平均工資查詢結果臨時表作為一張表,這樣查詢更簡單,普通子查詢就可解決
子查詢可獨立完成查詢
SELECT last_name, e1.department_id, e2.avg_salary FROM employees e1, ( SELECT avg(salary) avg_salary, department_id FROM employees GROUP BY department_id ) e2 -- 將查詢平均工資的臨時表作為一張表,作為 FROM 的表,注意:一定要給臨時表 別名 WHERE e1.department_id = e2.department_id AND e1.salary > e2.avg_salary;
或者:
SELECT e1.last_name, e1.department_id, e1.salary, e2.avg_salary FROM employees e1, (SELECT avg(salary) avg_salary, department_id FROM employees WHERE department_id GROUP BY department_id ) e2 -- 將查詢平均工資的臨時表作為一張表,作為 FROM 的表,注意:一定要給臨時表 別名 WHERE e1.department_id = e2.department_id AND e1.salary > e2.avg_salary;
-- 顯示員工的employee_id,last_name和location。其中,
-- 若員工department_id與 location_id為1800的department_id相同,則location為’Canada’,其餘為’USA’
即:{department_id = (查詢 department_id where條件 location_id=1800)} 為Canada,剩下的為 USA
SELECT employee_id, last_name, (CASE WHEN department_id = ( SELECT department_id FROM departments WHERE location_id = 1800 ) THEN 'CANADA' ELSE 'USA' END) location FROM employees;
-- 在 ORDER BY 子句中使用單列子查詢
-- 問題:查詢員工的employee_id,last_name,要求按照員工的department_name排序
SELECT employee_id, last_name FROM employees e ORDER BY (SELECT department_name FROM departments WHERE e.department_id = departments.department_id);
相關子查詢
相關子查詢按照一行接一行的順序執行,主查詢的每一行都執行一次子查詢
子查詢中使用主查詢中的列
-- 查詢員工中工資大於本部門平均工資的員工的last_name,salary和其department_id
SELECT last_name, salary, department_id FROM employees e WHERE salary > ( SELECT AVG(salary) FROM employees WHERE e.department_id = department_id );
EXISTS 操作符應用舉例
-- 常規方式查詢,manager也是員工,也有員工id,員工的manager_id就是管理者的員工id,所以,員工id在manager_id
-- 佇列裡的就是管理者
SELECT employee_id, last_name, job_id, department_id FROM employees WHERE employee_id IN ( SELECT manager_id FROM employees );
方式二 :EXISTS 關鍵字
EXISTS 判斷返回 false 或 true,
SELECT employee_id, last_name, job_id, department_id FROM employees e WHERE exists( -- 子查詢只負責判斷true / false (底層返回true或false),單獨查詢子查詢無結果 SELECT 'x' -- 沒有具體的明確的查詢設麼,所以,任意字元即可,常規以 X 代替 FROM employees WHERE manager_id = e.employee_id );
NOT EXISTS 操作符應用舉例:
-- 查詢departments表中,不存在employees表中的部門的department_id 和 department_name
SELECT department_id, department_name FROM departments d WHERE NOT EXISTS( SELECT 'X' FROM employees e WHERE e.department_id = d.department_id )
刪除應用舉例
如:刪除資料表中重複的資料,重複的資料即:資料條數 > 1 ,count (*) > 1 ;
首先查詢出重複的資料
-- 刪除表employees中重複的資料,先查詢出數量大於 1 的記錄,按照 employee_id 刪除,所以還要查詢出employee_id SELECT employee_id,last_name FROM employees GROUP BY last_name HAVING count(*) > 1;
或者
-- 出現在 select 子句後的非分組函式,一定出現在 group by 子句後(MySQL中不會報錯,Oracle中如下句子直接報錯) DELETE FROM employees WHERE employee_id IN ( SELECT employee_id FROM ( -- 這一步重複的書寫,因為MySQL只能將結果臨時表當做一張表,並要求有自己的表名 e SELECT employee_id FROM employees GROUP BY last_name HAVING count(*) > 1 ) e );
本文來自部落格園,作者:Vermeer,轉載請註明原文連結:https://www.cnblogs.com/Alay/p/15171951.html