1. 程式人生 > 其它 >第09章 MySQL子查詢

第09章 MySQL子查詢

第09章 MySQL子查詢


子查詢指一個查詢語句巢狀在另一個查詢語句內部的查詢,這個特性從MySQL 4.1開始引入。

SQL 中子查詢的使用大大增強了 SELECT 查詢的能力,因為很多時候查詢需要從結果集中獲取資料,或者需要從同一個表中先計算得出一個數據結果,然後與這個資料結果(可能是某個標量,也可能是某個集合)進行比較。

1. 需求分析與問題解決

1.1 實際問題

現有解決方式:

#方式一:
SELECT salary
FROM employees
WHERE last_name = 'Abel';

SELECT last_name,salary
FROM employees
WHERE salary > 11000;

#方式二:自連線
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e1.last_name = 'Abel'
AND e1.`salary` < e2.`salary`
#方式三:子查詢
SELECT last_name,salary
FROM employees
WHERE salary > (
		SELECT salary
		FROM employees
		WHERE last_name = 'Abel'
		);

1.2 子查詢的基本使用

  • 子查詢的基本語法結構:
  • 子查詢(內查詢)在主查詢之前一次執行完成。
  • 子查詢的結果被主查詢(外查詢)使用 。
  • 注意事項
    • 子查詢要包含在括號內
    • 將子查詢放在比較條件的右側
    • 單行操作符對應單行子查詢,多行操作符對應多行子查詢

1.3 子查詢的分類

分類方式1:

我們按內查詢的結果返回一條還是多條記錄,將子查詢分為單行子查詢

多行子查詢

  • 單行子查詢

  • 多行子查詢

分類方式2:

我們按內查詢是否被執行多次,將子查詢劃分為相關(或關聯)子查詢不相關(或非關聯)子查詢

子查詢從資料表中查詢了資料結果,如果這個資料結果只執行一次,然後這個資料結果作為主查詢的條件進行執行,那麼這樣的子查詢叫做不相關子查詢。

同樣,如果子查詢需要執行多次,即採用迴圈的方式,先從外部查詢開始,每次都傳入子查詢進行查詢,然後再將結果反饋給外部,這種巢狀的執行方式就稱為相關子查詢。

2. 單行子查詢

2.1 單行比較操作符

操作符 含義
= equal to
> greater than
>= greater than or equal to
< less than
<= less than or equal to
<> not equal to

2.2 程式碼示例

題目:查詢工資大於149號員工工資的員工的資訊

題目:返回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);

題目:返回公司工資最少的員工的last_name,job_id和salary

SELECT last_name, job_id, salary
FROM   employees
WHERE  salary = 
                (SELECT MIN(salary)
                 FROM   employees);

題目:查詢與141號或174號員工的manager_id和department_id相同的其他員工的employee_id,manager_id,department_id

實現方式1:不成對比較

SELECT  employee_id, manager_id, department_id
FROM    employees
WHERE   manager_id IN
		  (SELECT  manager_id
                   FROM    employees
                   WHERE   employee_id IN (174,141))
AND     department_id IN 
		  (SELECT  department_id
                   FROM    employees
                   WHERE   employee_id IN (174,141))
AND	employee_id NOT IN(174,141);

實現方式2:成對比較

SELECT	employee_id, manager_id, department_id
FROM	employees
WHERE  (manager_id, department_id) IN
                      (SELECT manager_id, department_id
                       FROM   employees
                       WHERE  employee_id IN (141,174))
AND	employee_id NOT IN (141,174);

2.3 HAVING 中的子查詢

  • 首先執行子查詢。
  • 向主查詢中的HAVING 子句返回結果。

題目:查詢最低工資大於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);

2.4 CASE中的子查詢

在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;

2.5 子查詢中的空值問題

SELECT last_name, job_id
FROM   employees
WHERE  job_id =
                (SELECT job_id
                 FROM   employees
                 WHERE  last_name = 'Haas');

子查詢不返回任何行

2.5 非法使用子查詢

SELECT employee_id, last_name
FROM   employees
WHERE  salary =
                (SELECT   MIN(salary)
                 FROM     employees
                 GROUP BY department_id);

多行子查詢使用單行比較符

3. 多行子查詢

  • 也稱為集合比較子查詢
  • 內查詢返回多行
  • 使用多行比較操作符

3.1 多行比較操作符

操作符 含義
IN 等於列表中的任意一個
ANY 需要和單行比較操作符一起使用,和子查詢返回的某一個值比較
ALL 需要和單行比較操作符一起使用,和子查詢返回的所有值比較
SOME 實際上是ANY的別名,作用相同,一般常使用ANY

體會 ANY 和 ALL 的區別

3.2 程式碼示例

題目:返回其它job_id中比job_id為‘IT_PROG’部門任一工資低的員工的員工號、姓名、job_id 以及salary

題目:返回其它job_id中比job_id為‘IT_PROG’部門所有工資都低的員工的員工號、姓名、job_id以及salary

題目:查詢平均工資最低的部門id

#方式1:
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
			SELECT MIN(avg_sal)
			FROM (
				SELECT AVG(salary) avg_sal
				FROM employees
				GROUP BY department_id
				) dept_avg_sal
			)
#方式2:
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) <= ALL (
				SELECT AVG(salary) avg_sal
				FROM employees
				GROUP BY department_id
)

3.3 空值問題

SELECT last_name
FROM employees
WHERE employee_id NOT IN (
			SELECT manager_id
			FROM employees
			);

4. 相關子查詢

4.1 相關子查詢執行流程

如果子查詢的執行依賴於外部查詢,通常情況下都是因為子查詢中的表用到了外部的表,並進行了條件關聯,因此每執行一次外部查詢,子查詢都要重新計算一次,這樣的子查詢就稱之為關聯子查詢

相關子查詢按照一行接一行的順序執行,主查詢的每一行都執行一次子查詢。

說明:子查詢中使用主查詢中的列

4.2 程式碼示例

題目:查詢員工中工資大於本部門平均工資的員工的last_name,salary和其department_id

方式一:相關子查詢

方式二:在 FROM 中使用子查詢

SELECT last_name,salary,e1.department_id
FROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP BY department_id) e2
WHERE e1.`department_id` = e2.department_id
AND e2.dept_avg_sal < e1.`salary`;

from型的子查詢:子查詢是作為from的一部分,子查詢要用()引起來,並且要給這個子查詢取別名,
把它當成一張“臨時的虛擬的表”來使用。

在ORDER BY 中使用子查詢:

題目:查詢員工的id,salary,按照department_name 排序

SELECT employee_id,salary
FROM employees e
ORDER BY (
	  SELECT department_name
	  FROM departments d
	  WHERE e.`department_id` = d.`department_id`
	);

題目:若employees表中employee_id與job_history表中employee_id相同的數目不小於2,輸出這些相同id的員工的employee_id,last_name和其job_id

SELECT e.employee_id, last_name,e.job_id
FROM   employees e 
WHERE  2 <= (SELECT COUNT(*)
             FROM   job_history 
             WHERE  employee_id = e.employee_id);

4.3 EXISTS 與 NOT EXISTS關鍵字

  • 關聯子查詢通常也會和 EXISTS操作符一起來使用,用來檢查在子查詢中是否存在滿足條件的行。
  • 如果在子查詢中不存在滿足條件的行:
    • 條件返回 FALSE
    • 繼續在子查詢中查詢
  • 如果在子查詢中存在滿足條件的行:
    • 不在子查詢中繼續查詢
    • 條件返回 TRUE
  • NOT EXISTS關鍵字表示如果不存在某種條件,則返回TRUE,否則返回FALSE。

題目:查詢公司管理者的employee_id,last_name,job_id,department_id資訊

方式一:

SELECT employee_id, last_name, job_id, department_id
FROM   employees e1
WHERE  EXISTS ( SELECT *
                 FROM   employees e2
                 WHERE  e2.manager_id = 
                        e1.employee_id);

方式二:自連線

SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM   employees e1 JOIN employees e2
WHERE e1.employee_id = e2.manager_id;

方式三:

SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN (
		     SELECT DISTINCT manager_id
		     FROM employees
		     
		     );

題目:查詢departments表中,不存在於employees表中的部門的department_id和department_name

SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (SELECT 'X'
                  FROM   employees
                  WHERE  department_id = d.department_id);

4.4 相關更新

UPDATE table1 alias1
SET    column = (SELECT expression
                 FROM   table2 alias2
                 WHERE  alias1.column = alias2.column);

使用相關子查詢依據一個表中的資料更新另一個表的資料。

題目:在employees中增加一個department_name欄位,資料為員工對應的部門名稱

# 1)
ALTER TABLE employees
ADD(department_name VARCHAR2(14));

# 2)
UPDATE employees e
SET department_name =  (SELECT department_name 
	                       FROM   departments d
	                       WHERE  e.department_id = d.department_id);

4.4 相關刪除

 DELETE FROM table1 alias1
 WHERE column operator (SELECT expression
                        FROM   table2 alias2
                        WHERE  alias1.column = alias2.column);

使用相關子查詢依據一個表中的資料刪除另一個表的資料。

題目:刪除表employees中,其與emp_history表皆有的資料

DELETE FROM employees e
WHERE employee_id in  
           (SELECT employee_id
            FROM   emp_history 
            WHERE  employee_id = e.employee_id);

5. 拋一個思考題

問題:誰的工資比Abel的高?

解答:

#方式1:自連線
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e1.last_name = 'Abel'
AND e1.`salary` < e2.`salary`
#方式2:子查詢
SELECT last_name,salary
FROM employees
WHERE salary > (
		SELECT salary
		FROM employees
		WHERE last_name = 'Abel'
		);

問題:以上兩種方式有好壞之分嗎?

解答:自連線方式好!

題目中可以使用子查詢,也可以使用自連線。一般情況建議你使用自連線,因為在許多 DBMS 的處理過程中,對於自連線的處理速度要比子查詢快得多。

可以這樣理解:子查詢實際上是通過未知表進行查詢後的條件判斷,而自連線是通過已知的自身資料表進行條件判斷,因此在大部分 DBMS 中都對自連線處理進行了優化。

本文來自部落格園,作者:微笑帶你去,轉載請註明原文連結:https://www.cnblogs.com/wxdnq/p/15598169.html