本文整理MySQL中的子查詢,相比於其他的分組查詢、排序查詢和連接查詢等,子查詢略微複雜。本文按照子查詢出現的位置,將子查詢分爲主要的幾類,並以實際的案例進行介紹,以求用形象的語言來表達清楚子查詢。
本文重點介紹子查詢爲前五大部分,分頁查詢與聯合查詢比較簡單,只要記住關鍵字LIMIT
和UNION
,基本就能使用了,放在後兩部分分別介紹。
文章目錄
一、子查詢介紹
含義
出現在其他語句中的select語句,稱爲子查詢或內查詢(其他語句可以是增刪改等);外部的查詢語句,稱爲主查詢或外查詢。
分類
1.按子查詢出現的位置分類:
- select後:僅僅支持標量子查詢
- from後:支持表子查詢
- where或having後:支持標量子查詢(單行子查詢)、列子查詢(多行子查詢)以及行子查詢
- exists後(稱爲相關子查詢)
2.按結果了集的行列數分類,分爲如下幾類:
- 標量子查詢(結果集只有一行一列)
- 列子查詢(結果集只有一列多行)
- 行子查詢(結果集有一行多列)
- 表子查詢(結果集一般爲多行多列)
重點爲:位於where或者having後面的標量子查詢與列子查詢
二、WHERE或HAVING後的子查詢
1. 標量子查詢
將查詢結果作爲標量使用:
案例1:查詢員工表中工資比 Abel高的員工信息
SELECT *
FROM employees
WHERE salary > (
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
查詢時可以添加多個子查詢作爲篩選條件:
案例2:查詢員工表中,job_id與141號員工相同,salary比143號員工高的員工的姓名,job_id 和工資
SELECT last_name,job_id,salary
FROM employees
WHERE job_id = (
SELECT job_id
FROM employees
WHERE employee_id = 141
) AND salary>(
SELECT salary
FROM employees
WHERE employee_id = 143
);
案例3:查詢最低工資大於50號部門最低工資的部門id和其最低工資
#初步實現可以分三步走:
#(1)查詢50號部門的最低工資
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
#(2)查詢每個部門的最低工資
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
#(3)在(2)基礎上篩選,滿足min(salary)>(1)
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
);
2. 列子查詢
案例1:返回location_id是1400或1700的部門中的所有員工姓名
#(1)查詢location_id是1400或1700的部門編號
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)
#(2)查詢員工姓名,要求部門號是(1)列表中的某一個
SELECT last_name
FROM employees
WHERE department_id IN(
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)
);
案例2:返回其它工種中比job_id爲‘IT_PROG’工種任一工資低的員工的員工號、姓名、job_id 以及salary
分析:比任一工資低的,那麼只要低於最大的就行了。如a < any(10, 20, 30)等價於 a < max(10, 20, 30)
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
SELECT MAX(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<ANY(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
案例3:返回其它工種中比job_id爲‘IT_PROG’工種所有工資都低的員工 的員工號、姓名、job_id 以及salary
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
SELECT MIN(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<ALL(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
3. 行子查詢(瞭解)
行子查詢的結果集爲一行多列或多行多列
案例:查詢員工編號最小並且工資最高的員工信息
一般做法:
SELECT *
FROM employees
WHERE employee_id=(
SELECT MIN(employee_id)
FROM employees
)AND salary=(
SELECT MAX(salary)
FROM employees
);
行子查詢:
SELECT *
FROM employees
WHERE (employee_id,salary)=(
SELECT MIN(employee_id),MAX(salary) #結果集爲一行多列
FROM employees
);
三、SELECT後的子查詢
說明:select後的子查詢僅僅支持標量子查詢
案例:查詢每個部門的員工個數
效果如下圖,前面爲departments表的信息,而departments表中必定有些部門在employees表中沒有員工與其對應,則個數爲0,如下半部分。
#分兩步分析:
#(1)如下效果爲每個部門的個數都是總員工數
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
) 個數
FROM departments d;
#(2)篩選條件爲員工表的部門id等於部門表的部門id
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.`department_id`
) 個數
FROM departments d;
四、FROM後的子查詢
說明:將子查詢結果充當一張表,要求必須起別名,否則找不到
案例:查詢每個部門的平均工資的工資等級
分析:(1)查詢每個部門的平均工資
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#查詢工資等級表
SELECT * FROM job_grades;
(2) 連接(1)的結果集和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後的子查詢也稱爲相關子查詢。
語法爲:exists (完整的查詢語句)
結果:1或0
#舉例:以下查詢結果爲 1
SELECT EXISTS(SELECT employee_id FROM employees);
案例1:查詢有員工的部門名
分析:(1)查詢部門表的所有部門id和部門名
SELECT d.`department_id`, department_name
FROM departments d
(2)在該結果集上篩選出有員工的(可以查詢任意信息,因爲只要判斷是否存在即可)
SELECT d.`department_id`, department_name
FROM departments d
WHERE EXISTS(
SELECT *
FROM employees e
WHERE d.`department_id`=e.`department_id` #子查詢涉及到主查詢的字段,所以稱相關子查詢
);
能用EXISTS的絕對可以用IN來實現:
SELECT department_name
FROM departments d
WHERE d.`department_id` IN(
SELECT department_id
FROM employees
);
案例2:查詢沒有女朋友的男神信息
# IN 方法:
SELECT bo.*
FROM boys bo
WHERE bo.id NOT IN(
SELECT b.`boyfriend_id`
FROM beauty b
);
# EXISTS 方法:
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS (
SELECT *
FROM beauty b
WHERE b.`boyfriend_id` = bo.`id`
);
六、分頁查詢
分頁查詢(LIMIT)可以理解爲每次查詢一定數量的記錄。
應用場景:當要顯示的數據,一頁顯示不全,需要分頁提交sql請求。
語法:
select 查詢列表
from 表
【join type join 表2
on 連接條件
where 篩選條件
group by 分組字段
having 分組後的篩選
order by 排序的字段】
limit 【offset,】size;
注:offset爲要顯示條目的起始索引(起始索引從0開始)
size 要顯示的條目個數
特點:
- limit語句放在查詢語句的最後
- 對於要分頁顯示的條目(提供頁數page和條目數size),公式爲:select 查詢列表 from 表 limit (page-1)*size,size;
案例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 ;
七、聯合查詢
聯合查詢(UNION)含義:將多條查詢語句的結果合併爲一個結果,當有重複記錄時,默認自動去重
應用場景:要查詢的結果來自於多個表,且多個表沒有直接的連接關係,但查詢的信息一致
語法:
查詢語句1
union
查詢語句2
union
...
特點:
- 要求多條查詢語句的查詢列數是一致的!
- 要求多條查詢語句的查詢的每一列的類型和順序最好一致
- union關鍵字默認去重,如果使用union all 可以包含重複項
案例1:查詢部門編號>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;
案例2:查詢中國用戶中男性的編號與姓名以及外國用戶中年男性的用戶編號 與姓名
SELECT id,cname FROM t_ca WHERE csex='男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender='male';