子查詢主要分為相關子查詢和非相關子查詢,本次以例子的形式為大家分享如何做數據庫的子查詢
創建數據庫
CREATE DATABASE demo103
創建兩張表
CREATE TABLE t_class(
c_id INT AUTO_INCREMENT PRIMARY KEY,
c_name VARCHAR(50) NOT NULL
)
CREATE TABLE t_student(
s_id INT AUTO_INCREMENT PRIMARY KEY,
s_name VARCHAR(50) NOT NULL,
s_sex VARCHAR(20) DEFAULT'男',
s_classid INT,
CONSTRAINT FOREIGN KEY (s_classid) REFERENCES t_class(c_id)
)
-- 學生表是看不出有3班的,不代表沒有三班,這是為什么單獨把班級表抽出來的原因,而且學生的班級只能對應班級表中
-- 已經存在的班級,不能隨意輸入,因為做了外鍵約束。
-- 現在改掉1,2,3班 換成110班 120班 119班,那你還知道學生表中的班級id對應的是哪個班不?
-- 查詢學生姓名及對應班級名稱 涉及到2個表
-- 學生姓名可以從t_student 獲取,最多只能得到s_classid,班級名稱c——那么哪兒來,班級名稱要從 t_class獲取
SELECT s_name FROM t_student
SELECT c_name FROM t_class
-- 就可以使用子查詢 就是一個嵌套查詢 一個查詢中嵌入另外一個查詢 最多嵌入255條查詢
-- 首先from t_student這張表 然后找的s_name 和s_classid 這個時候一行一行去看,是不是每行都可以得到一個s_classid
-- 然后再到t_class表中去找c_name
-- 1.相關子查詢, 子查詢必須依賴於主查詢 ,子查詢單獨運行會報錯,依賴於主查詢的結果
-- select嵌套 只能返回單行單列
-- 每次主查詢 執行一次,子查詢也會執行一次,最終執行N+1次,效率低下,如果主查詢沒有提供數據,子查詢無法執行
-- select 嵌套只能返回單行單列
SELECT s_name,(SELECT c_name FROM t_class WHERE c_id=s_classid) FROM t_student
-- 2.非相關子查詢
-- 子查詢對主查詢沒有依賴 子查詢只會執行一次,只會在from的時候才執行,性能較高,能獨立運行,只是給主查詢提供條件值
-- from 嵌套 必須要給嵌套的子查詢表起別名,可返回多行多列數據
-- 查詢性別為女 並且姓名為張三的
SELECT * FROM (SELECT * FROM t_student WHERE s_sex='女') AS t1
WHERE t1.s_name='張三'
-- where 嵌套 執行2次,子查詢可以單獨運行,不依賴主查詢,只是給主查詢提供條件值
-- 查詢一班的學生有哪些
萬一 t_class 的 c_id 三班==1 ,→首先找到一班的c_id → where s_classid=一班的c_id
SELECT * FROM t_student WHERE s_classid=1
-- →首先找到一班的c_id select c_id from t_class where c_name='一班'
SELECT * FROM t_student WHERE s_classid=(SELECT c_id FROM t_class WHERE c_name='一班') -- 返回多行單列 (in 關鍵詞)
-- 實際上子查詢就是嵌套查詢
-- 嵌套位置
-- select | where | from
-- 查詢與張三一個班級的學生有哪些
-- 找出張三是哪個班
SELECT s_classid FROM t_student WHERE s_name='張三'
-- 找出張三同班 的 classid=1 的同學
SELECT s_name FROM t_student WHERE s_classid=1
SELECT * FROM t_student WHERE s_classid=(SELECT s_classid FROM t_student WHERE s_name='張三')
-- 查詢與張三一個班級的學生有哪些,結果不包含張三
SELECT * -- 3
FROM t_student -- 1
WHERE s_classid=(SELECT s_classid FROM t_student WHERE s_name='張三') -- 2
HAVING s_name!='張三' -- 4 where是在select之前,不能用做聚合函數的判斷
-- having是在selcet之后,而且還可以用在select里面的聚合函數
-- 並且是之后才執行,所以可以用在二次篩選,
-- 有聚合函數就必須要用having
AND s_name!='張三' -- 4 AND也可以
-- 名字比較混亂加別名
SELECT * -- 3
FROM t_student t2 -- 1
WHERE t2.s_classid=(SELECT t1.s_classid FROM t_student t1 WHERE t1.s_name='張三') -- 2
HAVING t2.s_name!='張三' -- 4
-- 查詢班上比平均分高的學生有哪些
-- @1 找到平均分
SELECT AVG(IFNULL(s_score,0)) FROM t_student
-- where s_score > 平均分
SELECT * FROM t_student WHERE s_score>( SELECT AVG(IFNULL(s_score,0)) FROM t_student)
-- 查詢每個班上比平均分高的學生有哪些
-- @1 找到每個班平均分
SELECT s_classid,AVG(IFNULL(s_score,0)) FROM t_student GROUP BY s_classid
-- where s_score > 平均分
SELECT *
FROM (SELECT *,AVG(IFNULL(s_score,0)) FROM t_student GROUP BY s_classid) t2
WHERE t2.s_score>(SELECT AVG(IFNULL(s_score,0)) FROM t_student)
-- 第二種
SELECT * FROM t_student t1
WHERE s_score>(SELECT AVG(s_score) FROM t_student t2 WHERE t2.s_classid=t1.s_classid GROUP BY s_classid)
-- 這個where也嵌套了一個相關子查詢,所以我們在這兒只是說明一個事:相關還是非相關跟位置沒關系
-- 查詢至少有一名學生的班級名稱
SELECT c_name FROM t_class WHERE c_id IN(1,2,3) -- 如果三班沒有學生呢,這是我們知道只有1,2,3班有學生
-- in 集合的概念,一堆,不能直接用等號嘛
SELECT s_classid FROM t_student -- 獲取到t_student 里面有出現的s_classid 有出現,代表至少有一名學生
SELECT c_id,c_name FROM t_class WHERE c_id IN(SELECT s_classid FROM t_student)
-- ANY有點類似IN | All,只是語法不一樣,用等號(where=) any必須結合子查詢來使用 ANY子查詢投影出來的結果
SELECT c_id,c_name FROM t_class WHERE c_id= ANY(SELECT s_classid FROM t_student)
SELECT c_name FROM t_class WHERE c_id= ANY(1,2,3) -- 錯誤 any必須結合子查詢來使用
-- 有兩個張三。查詢分數高於任意一個張三的所有學生
-- 張三的分數
SELECT s_score FROM t_student WHERE s_name='張三'
SELECT * FROM t_student WHERE s_score>ANY( SELECT s_score FROM t_student WHERE s_name='張三')
-- 還可以使用MIN
SELECT * FROM t_student WHERE s_score>(SELECT MIN(s_score) FROM t_student WHERE s_name='張三')
-- 查詢分數高於任意一個張三的所有學生 -- 使用all
SELECT * FROM t_student WHERE s_score>ALL(SELECT s_score FROM t_student WHERE s_name='張三')
-- any 和all 必須跟子查詢 不能單獨使用
-- 子查詢不管怎么變,都只有三種,在where中嵌套一個,form 一個,select一個