學習筆記 2021.12.9cont
2021.12.9
聚合函式
常見的聚合函式
具體的count的使用場景的比較(預設具體欄位沒有非空的)。這裡涉及到更多底層方面的東西,到後面索引的時候再去具體瞭解,這裡就大概知道各自的結果就可以了:
其實,對於MyISAM引擎的表是沒有區別的。這種引擎內部有一計數器在維護著行數。
Innodb引擎的表用count(*),count(1)直接讀行數,複雜度是O(n),因為innodb真的要去數一遍。但好於具體的count(列名)。
group by
需求:查詢各個不同部門的聚合資料。
示例:
此時也可以多個列同時group by,即在一個類中再細分,示例如下:
使用的注意事項:
- select中出現的非組函式的欄位必須宣告在group by中,反之則不是必然要求。
- group by宣告在from後面,where後面,order和limit前面。
- 在後面新增with rollup可以多顯示一個總體的計算後的結果。知道有這麼個東西就好,一般也不常用。
having的使用
作用:也是過濾資料的。
過濾分組:HAVING子句
- 行已經被分組。
- 使用了聚合函式。
- 滿足HAVING 子句中條件的分組將被顯示。
- HAVING 不能單獨使用,必須要跟 GROUP BY 一起使用。
顯示各個部門中最高工資比10000高的部門資訊。
使用tips:
- 如果過濾條件中使用了聚合函式,則必須用having來替換where,作為一種習慣去適應。但是如果沒有聚合函式,兩種都可以,但是推薦用where。
- 且having必須在group by後面。
下面的這種方式也是合理的,注意兩種篩選條件的區分:
但是這種方式也不推薦,最後還是按照規範來寫。
where和having的對比
- having的使用範圍更廣。
- 沒有聚合函式時,where的效率要比having好。
具體的分析後面涉及到底層的時候再去詳細瞭解。
優點 | 缺點 | |
---|---|---|
WHERE | 先篩選資料再關聯,執行效率高 | 不能使用分組中的計算函式進行篩選 |
HAVING | 可以使用分組中的計算函式 | 在最後的結果集中進行篩選,執行效率較低 |
select的執行過程
常見的sql99的全部包含的語法:
但執行的順序卻是如下,而不是上面的編寫順序
1. 關鍵字的順序是不能顛倒的:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
2.SELECT 語句的執行順序(在 MySQL 和 Oracle 中,SELECT 執行順序基本相同):
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的欄位 -> DISTINCT -> ORDER BY -> LIMIT
比如你寫了一個 SQL 語句,那麼它的關鍵字順序和執行順序是下面這樣的:
SELECT DISTINCT player_id, player_name, count(*) as num # 順序 5
FROM player JOIN team ON player.team_id = team.team_id # 順序 1
WHERE height > 1.80 # 順序 2
GROUP BY player.team_id # 順序 3
HAVING num > 2 # 順序 4
ORDER BY num DESC # 順序 6
LIMIT 2 # 順序 7
在 SELECT 語句執行這些步驟的時候,每個步驟都會產生一個虛擬表
,然後將這個虛擬表傳入下一個步驟中作為輸入。需要注意的是,這些步驟隱含在 SQL 的執行過程中,對於我們來說是不可見的。
- 即是先找表,再分組和篩選。然後再按照select的條件來呈現。
- 這時就可以去理解select和having的功能區別了,where在遍歷的過程中即實行了篩選的操作,這樣在後面的group和having操作就可以省略很多,但是如果在having中進行實現的話,則會對所有的分組進行計算,但是實際上也不需要其他組的計算。
子查詢
即理解為查詢的巢狀。子查詢指一個查詢語句巢狀在另一個查詢語句內部的查詢,這個特性從MySQL 4.1開始引入。
SQL 中子查詢的使用大大增強了 SELECT 查詢的能力,因為很多時候查詢需要從結果集中獲取資料,或者需要從同一個表中先計算得出一個數據結果,然後與這個資料結果(可能是某個標量,也可能是某個集合)進行比較。
比如下面這種問題即是符合子查詢的場景:
子查詢的寫法:
即可以直觀的看到是查詢的巢狀。
- 子查詢的基本語法結構:
- 子查詢(內查詢)在主查詢之前一次執行完成。
- 子查詢的結果被主查詢(外查詢)使用 。
- 注意事項
- 子查詢要包含在括號內
- 將子查詢放在比較條件的右側
- 單行操作符對應單行子查詢,多行操作符對應多行子查詢
子查詢的分類
單行子查詢和多行子查詢。即內查詢的結果是一個還是多個。
相關子查詢和不相關子查詢。即內查詢是否被執行多次。相關子查詢的需求:查詢工資是否大於本部門平均工資的員工資訊。
單行子查詢
最好理解的一種
單行比較操作符
操作符 | 含義 |
---|---|
= | equal to |
> | greater than |
>= | greater than or equal to |
< | less than |
<= | less than or equal to |
<> | not equal to |
這裡就看一個例子就行
Q:查詢與141號員工的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)
AND department_id IN
(SELECT department_id
FROM employees
WHERE employee_id IN (141)
AND employee_id NOT IN(141);
having中的子查詢
直接看例子
Q:查詢最低工資大於50號部門最低工資的部門id和其最低工資
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) >
(SELECT MIN(salary)
FROM employees
WHERE department_id = 50);```
case中的子查詢
題目:顯式員工的employee_id,last_name和location。其中,若員工department_id與location_id為1800的department_id相同,則location為’Canada’,其餘則為’USA’。
SELECT employee_id, last_name,
(CASE department_id
WHEN
(SELECT department_id FROM departments
WHERE location_id = 1800)
THEN 'Canada' ELSE 'USA' END) location
FROM employees;
其他需要注意的
子查詢中的空值問題
SELECT last_name, job_id
FROM employees
WHERE job_id =
(SELECT job_id
FROM employees
WHERE last_name = 'Haas');
子查詢不返回任何行
非法使用子查詢
SELECT employee_id, last_name
FROM employees
WHERE salary =
(SELECT MIN(salary)
FROM employees
GROUP BY department_id);
多行子查詢使用單行比較符
多行子查詢
- 也稱為集合比較子查詢
- 內查詢返回多行
- 使用多行比較操作符
多行比較操作符
操作符 | 含義 |
---|---|
IN | 等於列表中的任意一個 |
ANY | 需要和單行比較操作符一起使用,和子查詢返回的某一個值比較 |
ALL | 需要和單行比較操作符一起使用,和子查詢返回的所有值比較 |
SOME | 實際上是ANY的別名,作用相同,一般常使用ANY |
注意此時in對於單行查詢自然也是可以的。
例子
返回其它job_id中比job_id為‘IT_PROG’部門所有工資都低的員工的員工號、姓名、job_id以及salary
SELECT employee_id,last_name,job_id,salaryFROM employeesWHERE job_id <> 'IT_PROG'AND salary < ALL( SELECT salary FROM employees WHERE job_id = 'IT_PROG' );
查詢平均工資最低的部門id
注意聚合函式是沒法去巢狀的。
SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary)=(SELECT MIN(asal)FROM(SELECT department_id,AVG(salary) asalFROM employeesGROUP BY department_id) dd);
這種情況注意一點就是因為group by後生成的也是一系列的資料,可以將其看作資料去比較,也自然可以看作表填到from後面的子迴圈裡面,這也是上面方法的思路。