.Net程式員學用Oracle系列(14):子查詢、集合查詢

来源:http://www.cnblogs.com/hanzongze/archive/2017/02/09/Oracle-Subquery.html
-Advertisement-
Play Games

1、 " 子查詢 " 1.1、 "子查詢簡介" 1.2、 "WITH 子查詢" 2、 " 集合查詢 " 2.1、 "UNION 和 UNION ALL" 2.2、 "MINUS" 2.3、 "INTERSECT" 2.4、 "集合運算與 ORDER BY" 3、 " DISTINCT 子句 " 3. ...


1、子查詢

1.1、子查詢簡介

子查詢是一個嵌套在 SELECT、INSERT、UPDATE 或 DELETE 語句或其他子查詢中的查詢。任何允許使用表達式的地方都可以使用子查詢,換句話說,子查詢幾乎可以出現在一條 SQL 語句的任意位置上,且必須用一對小括弧來包裹子查詢的定義。本系列博客的第 12 篇的 4.2 節已經演示了常見的三種子查詢寫法。子查詢也稱為內部查詢或內部選擇,而包含子查詢的語句也稱為外部查詢或外部選擇。

本人查閱了大量資料,似乎根本就找不到一種“正確的子查詢分類方法”。常見的分類名有:單行子查詢、多行子查詢、多列子查詢、相關子查詢、標量子查詢、內聯視圖等,前三個顧名思義,後三個分別簡要說明如下:

  • 相關子查詢:子查詢引用了外部查詢中包含的一列或多列,也就是說內部查詢得依靠外部查詢獲得值,這樣的子查詢被稱為相關子查詢,也稱為重覆子查詢。
  • 標量子查詢:只返回單一值的子查詢稱為標量子查詢。
  • 內聯視圖:一般是指寫在 FROM 子句後面的子查詢,它本質上是視圖,也稱內嵌視圖,與標準視圖的區別主要是無需事先編寫創建視圖的語句,便於執行查詢。

下麵我再補充幾個 WHERE 子句帶子查詢的示例:

-- 查詢低收入員工的姓名和生日
SELECT t.staff_name,t.birthday FROM demo.t_staff t 
WHERE EXISTS(SELECT 1 FROM demo.t_staff_low n WHERE n.staff_id=t.staff_id);

-- 查詢崗位工資最高的員工的姓名和生日
SELECT t.staff_name,t.birthday FROM demo.t_staff t 
WHERE t.post_salary = (SELECT MAX(n.post_salary) FROM demo.t_staff n);

-- 查詢崗位工資高於任何一個部門平均工資的員工姓名和生日
SELECT t.staff_name,t.birthday FROM demo.t_staff t 
WHERE t.post_salary > ALL(SELECT AVG(n.post_salary) FROM demo.t_staff n GROUP BY n.dept_code);

-- 查詢測試部所有女生的姓名和生日(一般沒人這麼寫,只為演示)
SELECT t.staff_name,t.birthday FROM demo.t_staff t 
WHERE (t.dept_code,t.gender) = (SELECT '010104',0 FROM DUAL);

-- 查詢任何低收入部門的女生的姓名和生日
SELECT t.staff_name,t.birthday FROM demo.t_staff t 
WHERE (t.dept_code,t.gender) = ANY(SELECT n.dept_code,0 FROM demo.t_staff_low n);

-- 查詢部門平均崗位工資高於公司平均崗位工資的部門人數和平均崗位工資
SELECT t.dept_code,COUNT(1) count_staff,AVG(t.post_salary) avg_salary 
FROM demo.t_staff t 
GROUP BY t.dept_code 
HAVING AVG(t.post_salary)>(SELECT AVG(n.post_salary) FROM demo.t_staff n) 
ORDER BY t.dept_code;

1.2、WITH 子查詢

WITH 子查詢的作用類似於內聯視圖,包括內聯視圖在內的其它子查詢都只能夠引用一次;而 WITH 子查詢需要在引用之前先定義,一旦定義了在整個查詢的後續部分就可以按名稱來反覆引用,從這點來看又很像臨時表。Oracle 從 11g R2 開始支持遞歸的 WITH,即允許在 WITH 子查詢的定義中對自身引用,而其它資料庫,如 (MS)SQL Server、PostgreSQL、DB2 等都先於 Oracle 支持這一特性。語法示例:

WITH query_name AS(SELECT ...)
SELECT ...

案例一:查詢年齡在25歲及以下,固定工資在5000及以上的員工基本信息。示例:

WITH t AS(
  SELECT t1.staff_name,t2.enum_name dept_name,DECODE(t1.gender,1,'男',0,'女','未知') gender,
    EXTRACT(YEAR FROM fn_now)-EXTRACT(YEAR FROM t1.birthday) age,base_salary+post_salary fixed_salary
  FROM demo.t_staff t1
  LEFT JOIN demo.t_field_enum t2 ON t1.dept_code=t2.enum_code AND t2.field_code='DEPT'
  WHERE t1.is_disabled=0
) 
SELECT t.dept_name,t.staff_name,t.gender,t.age,t.fixed_salary 
FROM t WHERE t.age<=25 AND t.fixed_salary>=5000

結果:

DEPT_NAME                                          STAFF_NAME                                         GENDER        AGE FIXED_SALARY
-------------------------------------------------- -------------------------------------------------- ------ ---------- ------------
研發一部                                           大國                                               男             25         6500
研發三部                                           小玲                                               女             23         5400
研發三部                                           韓三                                               男             24         7550
測試部                                             小燕                                               女             25         5600

案例二:統計個個部門的人數、總工資、平均工資、最高工資、最低工資。示例:

WITH
t1 AS (SELECT n.dept_code FROM demo.t_staff n GROUP BY n.dept_code),
t2 AS (SELECT n.enum_code dept_code,n.enum_name dept_name FROM demo.t_field_enum n WHERE n.field_code='DEPT'),
t3 AS (SELECT t.dept_code,COUNT(1) count_staff FROM demo.t_staff_salary t GROUP BY t.dept_code),
t4 AS (SELECT t.dept_code,SUM(t.fixed_salary) sum_salary FROM demo.t_staff_salary t GROUP BY t.dept_code),
t5 AS (SELECT t.dept_code,AVG(t.fixed_salary) avg_salary FROM demo.t_staff_salary t GROUP BY t.dept_code),
t6 AS (SELECT t.dept_code,MAX(t.fixed_salary) max_salary FROM demo.t_staff_salary t GROUP BY t.dept_code),
t7 AS (SELECT t.dept_code,MIN(t.fixed_salary) min_salary FROM demo.t_staff_salary t GROUP BY t.dept_code)
SELECT t2.dept_name,t3.count_staff,t4.sum_salary,t5.avg_salary,t6.max_salary,t7.min_salary
FROM t1,t2,t3,t4,t5,t6,t7
WHERE t1.dept_code=t2.dept_code AND t1.dept_code=t3.dept_code AND t1.dept_code=t4.dept_code 
AND t1.dept_code=t5.dept_code AND t1.dept_code=t6.dept_code AND t1.dept_code=t7.dept_code
ORDER BY t1.dept_code;

結果:

DEPT_NAME                                          COUNT_STAFF SUM_SALARY AVG_SALARY MAX_SALARY MIN_SALARY
-------------------------------------------------- ----------- ---------- ---------- ---------- ----------
研發一部                                                     4      36002     9000.5      10501       6500
研發二部                                                     2      18500       9250      10000       8500
研發三部                                                     2      12950       6475       7550       5400
測試部                                                       2      12700       6350       7100       5600
實施一部                                                     3      18700 6233.33333       8500       5100
實施二部                                                     2      15700       7850       8000       7700

案例二不使用 WITH 的示例:

SELECT t2.enum_name dept_name,t3.count_staff,t4.sum_salary,t5.avg_salary,t6.max_salary,t7.min_salary
FROM(SELECT n.dept_code FROM demo.t_staff n GROUP BY n.dept_code) t1
LEFT JOIN demo.t_field_enum t2 ON t1.dept_code=t2.enum_code AND t2.field_code='DEPT'
LEFT JOIN(SELECT t.dept_code,COUNT(1) count_staff FROM demo.t_staff_salary t GROUP BY t.dept_code) t3 
  ON t1.dept_code=t3.dept_code
LEFT JOIN(SELECT t.dept_code,SUM(t.fixed_salary) sum_salary FROM demo.t_staff_salary t GROUP BY t.dept_code) t4 
  ON t1.dept_code=t4.dept_code
LEFT JOIN(SELECT t.dept_code,AVG(t.fixed_salary) avg_salary FROM demo.t_staff_salary t GROUP BY t.dept_code) t5 
  ON t1.dept_code=t5.dept_code
LEFT JOIN(SELECT t.dept_code,MAX(t.fixed_salary) max_salary FROM demo.t_staff_salary t GROUP BY t.dept_code) t6 
  ON t1.dept_code=t6.dept_code
LEFT JOIN(SELECT t.dept_code,MIN(t.fixed_salary) min_salary FROM demo.t_staff_salary t GROUP BY t.dept_code) t7 
  ON t1.dept_code=t7.dept_code
ORDER BY t1.dept_code;

2、集合查詢

在數學中可以對集合做交並差運算,在 SQL 中同樣可以對查詢結果集做交並差操作。這三種 SQL 集合查詢對應的操作符關鍵字分別是 INTERSECT、UNION/UNION ALL、MINUS。

2.1、UNION 和 UNION ALL

最常見的 SQL 集合查詢就是並集查詢,並集查詢操作符有 UNION 和 UNION ALL 兩個,用法完全一致。示例:

-- 查詢本科學歷的女生和 93 後的員工
SELECT t1.staff_name,t1.gender,t1.edu_bg,t1.birthday FROM demo.t_staff t1 WHERE t1.gender=0 AND t1.edu_bg=1
UNION ALL
SELECT t2.staff_name,t2.gender,t2.edu_bg,t2.birthday FROM demo.t_staff t2 WHERE t2.birthday>=TO_DATE('1993-01-01','yyyy-mm-dd');

結果:

STAFF_NAME                                         GENDER EDU_BG BIRTHDAY
-------------------------------------------------- ------ ------ -----------
小玲                                                    0      1 1994-06-17
韓三                                                    1      1 1993-08-18
小玲                                                    0      1 1994-06-17

從 UNION 和 UNION ALL 的查詢結果來看,它們的區別之一就是:UNION 會去除重覆行,而 UNION ALL 會保留重覆行,如果把上例中的 ALL 關鍵字去掉,第 1、3 行中就會有 1 行被當作重覆行去除;區別之二就是:結果集預設的排序不同,UNION ALL 只是按關聯次序來組織數據,不再排序,而 UNION 將會按預設規則對整個數據集進行排序。另外,UNION ALL 的運算效率比 UNION 要高,故此,UNION ALL 也相對常用一些。

2.2、MINUS

可通過 MINUS 操作符求兩個結果集的差集。差集結果集不包括重覆行,且會按預設規則排序。示例:

-- 查詢 demo.t_staff 表中有而 demo.t_staff_copy 表中沒有的員工
SELECT t1.staff_name,t1.gender,t1.birthday FROM demo.t_staff t1
MINUS
SELECT t2.staff_name,t2.gender,t2.birthday FROM demo.t_staff_copy t2;

結果:

STAFF_NAME                                         GENDER BIRTHDAY
-------------------------------------------------- ------ -----------
大國                                                    1 1992-01-15
李陽                                                    1 1989-01-14
徐來                                                    1 1991-04-01

2.3、INTERSECT

可通過 INTERSECT 操作符求兩個結果集的交集。交集結果集不包括重覆行,且會按預設規則排序。示例:

-- 查詢年輕人中的低收入者
SELECT t1.staff_name,t1.gender,t1.dept_code FROM demo.t_staff_young t1
INTERSECT
SELECT t2.staff_name,t2.gender,t2.dept_code FROM demo.t_staff_low t2;

結果:

STAFF_NAME                                         GENDER DEPT_CODE
-------------------------------------------------- ------ --------------------
王二                                                    1 010101
小紅                                                    0 010201

2.4、集合運算與 ORDER BY

無論是交並差中的那種集合查詢,只需要在最後一個查詢的後面加上 ORDER BY 子句,即可對整個結果集排序。示例:

SELECT t1.staff_name,t1.gender,t1.dept_code FROM demo.t_staff t1 WHERE t1.gender=0 AND ROWNUM <= 1
UNION ALL
SELECT t2.staff_name,t2.gender,t2.dept_code FROM demo.t_staff_young t2 WHERE ROWNUM <= 1
UNION ALL
SELECT t3.staff_name,t3.gender,t3.dept_code FROM demo.t_staff_low t3 WHERE t3.gender=0
ORDER BY dept_code;

結果:

STAFF_NAME                                         GENDER DEPT_CODE
-------------------------------------------------- ------ --------------------------------------------------
小強                                                    1 010101
小玲                                                    0 010103
小紅                                                    0 010201

有個需要註意的細節問題是:這個 ORDER BY 後的排序欄位不能加任何別名限定,這個也好理解,畢竟排序操作是針對整個結果集的。也就是說上例中 ORDER BY 後的 dept_code 不屬於 t1、t2、t3 中任何一個表,而是整個結果集的。那萬一 t1、t2、t3 表中的欄位名不相同的話,排序的欄位名又改如何確定呢?本人的測試結果是:它一定是第 1 個查詢里的欄位名或列別名,但前提是後面所有查詢的該列都是真實欄位名,或列別名與第 1 個查詢里的欄位名或列別名相同。如果你嫌這個規則太複雜也不好記,那麼也可以給所有查詢的該列取一個相同的別名。另外還可以利用 ORDER BY 的另一個語法規則:ORDER BY N,即直接寫排序欄位的列編號。如上例的查詢想要根據第 2 列來排序,這一這麼來寫:

SELECT t1.staff_name,t1.gender,t1.dept_code FROM demo.t_staff t1 WHERE t1.gender=0 AND ROWNUM <= 1
UNION ALL
SELECT t2.staff_name,t2.gender,t2.dept_code FROM demo.t_staff_young t2 WHERE ROWNUM <= 1
UNION ALL
SELECT t3.staff_name,t3.gender,t3.dept_code FROM demo.t_staff_low t3 WHERE t3.gender=0
ORDER BY 2;

結果:

STAFF_NAME                                         GENDER DEPT_CODE
-------------------------------------------------- ------ --------------------------------------------------
小玲                                                    0 010103
小紅                                                    0 010201
小強                                                    1 010101

3、DISTINCT 子句

DISTINCT 子句的作用就是把一組數據中的重覆行去掉,留下唯一的數據。

3.1、普通用法

示例:

SELECT DISTINCT t.dept_code FROM demo.t_staff t; -- 查詢所有部門,效果同下
SELECT t.dept_code FROM demo.t_staff t GROUP BY t.dept_code;

3.2、做聚合函數的參數

COUNT、SUM、MAX、MIN、AVG 五個常見聚合函數內部都支持 DISTINCT。
語法:

SELECT func(DISTINCT field) FROM table [WHERE conditions];

示例一:

-- 統計有女員工的部門數量
SELECT COUNT(DISTINCT t.dept_code) count_dept FROM demo.t_staff t WHERE t.gender=0;

示例二:

-- 統計所有員工的平均工資,若有多人工資相同則只統計其中一人的工資
SELECT AVG(DISTINCT t.fixed_salary) avg_salary FROM demo.t_staff_salary t;

4、總結

本文主要講述了 Oracle 的子查詢概念、WITH 子查詢、集合查詢以及集合去重語句 DISTINCT。其中子查詢是最重要的,不管用那種關係型資料庫,都得熟練掌握。而 WITH 子查詢和 DISTINCT 子句以及集合查詢也都是比較實用的 SQL 功能,最好也要掌握。

本文鏈接http://www.cnblogs.com/hanzongze/p/Oracle-Subquery.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.準備安裝源 下載地址: "https://www.postgresql.org/ftp/source/" 下載並解壓。 2.軟體編譯安裝 配置、檢查安裝環境 成功後,方可進入下一步。遇到問題參考 "[configure遇到的問題]" 編譯安裝 3.配置資料庫 內核參數配置 用戶配置 初始化資料庫 ...
  • USE [master] GO /****** Object: StoredProcedure [dbo].[p_comparestructure] Script Date: 02/09/2017 15:39:59 ******/ SET ANSI_NULLS ON GO SET QUOTED_ID... ...
  • 轉載地址:http://lobert.iteye.com/blog/1955841 前些天拿到一個表,將近有4000w數據,沒有任何索引,主鍵。(建這表的絕對是個人才) 這是一個日誌表,記錄了游戲中物品的產出與消耗,原先有一個後臺對這個表進行統計。。。。。(這要用超級電腦才能統計得出來吧),只能幫 ...
  • 在一個測試伺服器刪除發佈(Publication)時遇到下麵錯誤,具體如下所示 標題: Microsoft SQL Server Management Studio ------------------------------ Could not delete publication 'RPL_GE... ...
  • 磁碟問題定位基本流程: 磁碟的壓力分析,主要使用下麵幾個性能計數器 (針對單獨的物理盤,每個物理磁碟都會有一組): Avg. Disk Read Queue Length 讀隊列(越小越好,理想值 2 以下,隊列越高說明一個操作的響應時間越長) Avg. Disk Write Queue Lengt ...
  • 面向對象(Object Oriented,OO)。 起初,“面向對象”是指在程式設計中採用封裝、繼承、多態等設計方法。現在,面向對象的思想已經涉及到軟體開發的各個方面。如,面向對象的分析(OOA,ObjectOriented Analysis),面向對象的設計(OOD,Object Oriented ...
  • 1,開始 昨天在CSDN上看到這一個提問帖子:《用SQL寫一個存儲過程10進位轉33進位代碼 》,原帖的問題是這樣: 急求10進位轉33進位代碼。 就是我想在SQL中創建一個存儲過程“T10TO33”可以實現將10進位的數據轉換成33進位。 33進位規則是:1,2,3,4,5,6,7,8,9,A,B ...
  • select sys_guid() from dual; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...