1. 程式人生 > 其它 >MySQL高階 之 子查詢

MySQL高階 之 子查詢

>>> 多年前的 SQL 聯絡筆記,現在回頭看很多問題,也懶得整理了,直接搬運到 部落格,僅僅是一些練習題,並非知識庫 <<<

子查詢是巢狀在 SQL 語句中的另一個SELECT 語句

子查詢可以用在很多地方,如 where 後面 from 後面 group by 後面 order by後面、select後面等等

子查詢 (內查詢) 在主查詢執行之前執行

主查詢(外查詢)使用子查詢的結果:

1、問題:查詢工資大於149號員工工資的員工的資訊

SELECT *
FROM employees e
WHERE salary > (
    SELECT
salary FROM employees WHERE employee_id = 149

多列子查詢

主查詢與子查詢返回的多個列進行比較

列比較

多列子查詢中的比較分為兩種:

成對比較

不成對比較

2、問題:查詢與141號或174號員工的manager_iddepartment_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_id
FROM 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_namelocation。其中,

-- 若員工department_id location_id1800department_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