SQL 如何使用自聯結、自然聯結和外聯結

来源:https://www.cnblogs.com/vin-c/archive/2022/05/18/16283354.html
-Advertisement-
Play Games

本文介紹自聯結(self-join)、自然聯結(natural join)和外聯結 (outer join),包括它們的含義和使用方法。介紹如何使用表別名,如何對被聯結的表使用聚集函數。 一、使用表別名 SQL 如何創建計算欄位 介紹瞭如何使用別名引用被檢索的表列。給列起別名的語法如下: SELEC ...


目錄

本文介紹自聯結(self-join)、自然聯結(natural join)和外聯結 (outer join),包括它們的含義和使用方法。介紹如何使用表別名,如何對被聯結的表使用聚集函數。

一、使用表別名

SQL 如何創建計算欄位 介紹瞭如何使用別名引用被檢索的表列。給列起別名的語法如下:

SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
        AS vend_title
FROM Vendors
ORDER BY vend_name;

SQL 除了可以對列名和計算欄位使用別名,還允許給表名起別名。這樣做有兩個主要理由:

  • 縮短 SQL 語句;
  • 允許在一條 SELECT 語句中多次使用相同的表。

請看下麵的 SELECT 語句。它與 如何使用 SQL INNER JOIN 聯結兩個或多個表 例子中所用的語句基本相同,但改成了使用別名:

SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
 AND OI.order_num = O.order_num
 AND prod_id = 'RGAN01';

可以看到,FROM 子句中的三個表全都有別名。Customers AS C 使用 C 作為 Customers 的別名,如此等等。

這樣,就可以使用省略的 C 而不用全名 Customers

在這個例子中,表別名只用於 WHERE 子句。其實它不僅能用於 WHERE 子句,還可以用於 SELECT 的列表、ORDER BY 子句以及其他語句部分。

註意:Oracle 中沒有 AS

Oracle 不支持 AS 關鍵字。

要在 Oracle 中使用別名,可以不用 AS,簡單地指定列名即可(因此,應該是 Customers C,而不是 Customers AS C)。

需要註意,表別名只在查詢執行中使用。與列別名不一樣,表別名不返回到客戶端。

二、使用不同類型的聯結

迄今為止,我們使用的只是內聯結或等值聯結的簡單聯結。現在來看三種其他聯結:自聯結(self-join)、自然聯結(natural join)和外聯結 (outer join)。

2.1 自聯結

如前所述,使用表別名的一個主要原因是能在一條 SELECT 語句中不止一次引用相同的表。下麵舉一個例子。

假如要給與 Jim Jones 同一公司的所有顧客發送一封信件。這個查詢要求首先找出 Jim Jones 工作的公司,然後找出在該公司工作的顧客。

下麵是解決此問題的一種方法:

SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
                   FROM Customers
                   WHERE cust_contact = 'Jim Jones');

輸出:

cust_id      cust_name          cust_contact
--------     --------------     --------------
1000000003   Fun4All            Jim Jones
1000000004   Fun4All            Denise L. Stephens

這是第一種解決方案,使用了子查詢。內部的 SELECT 語句做了一個簡單檢索,返回 Jim Jones 工作公司的 cust_name

該名字用於外部查詢的 WHERE 子句中,以檢索出為該公司工作的所有雇員(SQL 如何使用子查詢 中介紹了子查詢,更多信息請參閱此文章)。

現在來看使用聯結的相同查詢:

SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
 AND c2.cust_contact = 'Jim Jones';

輸出:

cust_id     cust_name       cust_contact
-------     -----------     --------------
1000000003  Fun4All         Jim Jones
1000000004  Fun4All         Denise L. Stephens

提示:Oracle 中沒有 AS

Oracle 用戶應該記住去掉 AS

此查詢中需要的兩個表實際上是相同的表,因此 Customers 表在 FROM 子句中出現了兩次。

雖然這是完全合法的,但對 Customers 的引用具有歧義性,因為 DBMS 不知道你引用的是哪個 Customers 表。

解決此問題,需要使用表別名。Customers 第一次出現用了別名 c1,第二次出現用了別名 c2。現在可以將這些別名用作表名。

例如,SELECT 語句使用 c1 首碼明確給出所需列的全名。

如果不這樣,DBMS 將返回錯誤,因為名為 cust_idcust_namecust_contact 的列各有兩個。DBMS 不知道想要的是哪一列(即使它們其實是同一列)。

WHERE 首先聯結兩個表,然後按第二個表中的 cust_contact 過濾數據,返回所需的數據。

提示:用自聯結而不用子查詢

自聯結通常作為外部語句,用來替代從相同表中檢索數據的使用子查詢語句。

雖然最終的結果是相同的,但許多 DBMS 處理聯結遠比處理子查詢快得多。

應該試一下兩種方法,以確定哪一種的性能更好。

2.2 自然聯結

無論何時對錶進行聯結,應該至少有一列不止出現在一個表中(被聯結的列)。

標準的聯結(如何使用 SQL INNER JOIN 聯結兩個或多個表 中介紹的內聯結)返回所有數據,相同的列甚至多次出現。

自然聯結排除多次出現,使每一列只返回一次。

怎樣完成這項工作呢?答案是,系統不完成這項工作,由你自己完成它。

自然聯結要求你只能選擇那些唯一的列,一般通過對一個表使用通配符(SELECT *),而對其他表的列使用明確的子集來完成。下麵舉一個例子:

SELECT C.*, O.order_num, O.order_date,
       OI.prod_id, OI.quantity, OI.item_price
FROM Customers AS C, Orders AS O,
     OrderItems AS OI
WHERE C.cust_id = O.cust_id
 AND OI.order_num = O.order_num
 AND prod_id = 'RGAN01';

提示:Oracle 中沒有 AS

Oracle 用戶應該記住去掉 AS

在這個例子中,通配符只對第一個表使用。所有其他列明確列出,所以沒有重覆的列被檢索出來。

事實上,我們迄今為止建立的每個內聯結都是自然聯結,很可能永遠都不會用到不是自然聯結的內聯結。

2.3 外聯結

許多聯結將一個表中的行與另一個表中的行相關聯,但有時候需要包含沒有關聯行的那些行。例如,可能需要使用聯結完成以下工作:

  • 對每個顧客下的訂單進行計數,包括那些至今尚未下訂單的顧客;
  • 列出所有產品以及訂購數量,包括沒有人訂購的產品;
  • 計算平均銷售規模,包括那些至今尚未下訂單的顧客。

在上述例子中,聯結包含了那些在相關表中沒有關聯行的行。這種聯結稱為外聯結。

註意:語法差別

需要註意,用來創建外聯結的語法在不同的 SQL 實現中可能稍有不同。

下麵段落中描述的各種語法形式覆蓋了大多數實現,在繼續學習之前請參閱你使用的 DBMS 文檔,以確定其語法。

下麵的 SELECT 語句給出了一個簡單的內聯結。它檢索所有顧客及其訂單:

SELECT Customers.cust_id, Orders.order_num
FROM Customers
 INNER JOIN Orders ON Customers.cust_id = Orders.cust_id;

外聯結語法類似。要檢索包括沒有訂單顧客在內的所有顧客,可如下進行:

SELECT Customers.cust_id, Orders.order_num
FROM Customers
 LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;

輸出:

cust_id        order_num
----------     ---------
1000000001     20005
1000000001     20009
1000000002     NULL
1000000003     20006
1000000004     20007
1000000005     20008

類似 如何使用 SQL INNER JOIN 聯結兩個或多個表 提到的內聯結,這條 SELECT 語句使用了關鍵字 OUTER JOIN 來指定聯結類型(而不是在 WHERE 子句中指定)。

但是,與內聯結關聯兩個表中的行不同的是,外聯結還包括沒有關聯行的行。

在使用 OUTER JOIN 語法時,必須使用 RIGHTLEFT 關鍵字指定包括其所有行的表(RIGHT 指出的是 OUTER JOIN 右邊的表,而 LEFT 指出的是 OUTER JOIN 左邊的表)。

上面的例子使用 LEFT OUTER JOINFROM 子句左邊的表(Customers 表)中選擇所有行。

為了從右邊的表中選擇所有行,需要使用 RIGHT OUTER JOIN,如下例所示:

SELECT Customers.cust_id, Orders.order_num
FROM Customers
 RIGHT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;

註意:SQLite 外聯結

SQLite 支持 LEFT OUTER JOIN,但不支持 RIGHT OUTER JOIN

幸好,如果你確實需要在 SQLite 中使用 RIGHT OUTER JOIN,有一種更簡單的辦法,這將在下麵的提示中介紹。

提示:外聯結的類型

要記住,總是有兩種基本的外聯結形式:左外聯結和右外聯結。

它們之間的唯一差別是所關聯的表的順序。

換句話說,調整 FROMWHERE 子句中表的順序,左外聯結可以轉換為右外聯結。

因此,這兩種外聯結可以互換使用,哪個方便就用哪個。

還存在另一種外聯結,就是全外聯結(full outer join),它檢索兩個表中的所有行並關聯那些可以關聯的行。

與左外聯結或右外聯結包含一個表的不關聯的行不同,全外聯結包含兩個表的不關聯的行。全外聯結的語法如下:

SELECT Customers.cust_id, Orders.order_num
FROM Customers
 FULL OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;

註意:FULL OUTER JOIN 的支持

MariaDB、MySQL 和 SQLite 不支持 FULL OUTER JOIN 語法。

三、使用帶聚集函數的聯結

如何使用 SQL AVG、COUNT、MAX、MIN 和 SUM 彙總數據 所述,聚集函數用來彙總數據。

雖然至今為止我們舉的聚集函數的例子都只是從一個表中彙總數據,但這些函數也可以與聯結一起使用。

我們來看個例子,要檢索所有顧客及每個顧客所下的訂單數,下麵的代碼使用 COUNT() 函數完成此工作:

SELECT Customers.cust_id,
       COUNT(Orders.order_num) AS num_ord
FROM Customers
 INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

輸出:

cust_id        num_ord
----------     --------
1000000001     2
1000000003     1
1000000004     1
1000000005     1

這條 SELECT 語句使用 INNER JOINCustomersOrders 表互相關聯。

GROUP BY 子句按顧客分組數據,因此,函數調用 COUNT(Orders.order_num) 對每個顧客的訂單計數,將它作為 num_ord 返回。

聚集函數也可以方便地與其他聯結一起使用。請看下麵的例子:

SELECT Customers.cust_id,
       COUNT(Orders.order_num) AS num_ord
FROM Customers
 LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

輸出:

cust_id        num_ord
----------     -------
1000000001     2
1000000002     0
1000000003     1
1000000004     1
1000000005     1

這個例子使用左外部聯結來包含所有顧客,甚至包含那些沒有任何訂單的顧客。結果中也包含了顧客 1000000002,他有 0 個訂單,這和使用 INNER JOIN 時不同。

四、使用聯結和聯結條件

在總結討論聯結的這兩篇前,有必要彙總一下聯結及其使用的要點。

  • 註意所使用的聯結類型。一般我們使用內聯結,但使用外聯結也有效。
  • 關於確切的聯結語法,應該查看具體的文檔,看相應的 DBMS 支持何種語法(大多數 DBMS 使用這兩篇中描述的某種語法)。
  • 保證使用正確的聯結條件(不管採用哪種語法),否則會返回不正確的數據。
  • 應該總是提供聯結條件,否則會得出笛卡兒積。
  • 在一個聯結中可以包含多個表,甚至可以對每個聯結採用不同的聯結類型。雖然這樣做是合法的,一般也很有用,但應該在一起測試它們前分別測試每個聯結。這會使故障排除更為簡單。

五、小結

本文是 如何使用 SQL INNER JOIN 聯結兩個或多個表 的延續,首先介紹瞭如何以及為什麼使用別名,然後討論不同的聯結類型以及每類聯結所使用的語法。

我們還介紹瞭如何與聯結一起使用聚集函數,以及在使用聯結時應該註意的問題。

原文鏈接:https://www.developerastrid.com/sql/sql-joins/

(完)


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

-Advertisement-
Play Games
更多相關文章
  • ps命令 ps命令來自於英文片語”process status“的縮寫,其功能是用於顯示當前系統的進程狀態。使用ps命令可以查看到進程的所有信息,例如進程的號碼、發起者、系統資源使用占比(處理器與記憶體)、運行狀態等等。幫助我們及時的發現哪些進程出現”僵死“或”不可中斷“等異常情況。 經常會與kill ...
  • 一、概述 在眾多 Hadoop 版本中, CDH(Cloudera Hadoop) 是 Hadoop 眾多分支中比較出色的版本, 它由Cloudera 發行和維護。CDH 基於 Apache 的 Hadoop 進行重新構建,提供了基於 Web 頁面的群集部署和管理操作。Hadoop發行版除了社區的A ...
  • SpringDataRedis的序列化的一些問題 RedisTemplate可以接收任意Object作為值寫入Redis,但是如果不實現設置序列化器的化預設是採用JDK序列化,序列化後的結果可讀性差並且記憶體占用空間大,如下圖。 自定義RedisTemplate的序列化方式 key和 hashKey採 ...
  • 導讀: 今天和大家分享京東零售OLAP平臺的建設和場景的實踐,主要包括四大部分: 管控面建設 優化技巧 典型業務 大促備戰 -- 01 管控面建設 1. 管控面介紹 管控面可以提供高可靠高效可持續運維保障、快速部署小時交付的能力,尤其是針對ClickHouse這種運維較弱但是性能很高的OLAP核心引 ...
  • 本文介紹如何使用 UPDATE 和 DELETE 語句處理表中的數據,還介紹了為什麼 WHERE 子句對 UPDATE 和 DELETE 語句很重要。 一、更新數據 更新(修改)表中的數據,可以使用 UPDATE 語句。有兩種使用 UPDATE 的方式: 更新表中的特定行; 更新表中的所有行。 下麵 ...
  • 本文介紹如何使用 SQL INSERT 語句將數據插入到表中,如何用 INSERT SELECT 從其他表中導入行,如何用 SELECT INTO 將行導出到一個新表。 一、數據插入 毫無疑問,SELECT 是最常用的 SQL 語句了。但是,還有其他 3 個常用的 SQL 語句需要學習。第一個就是 ...
  • i,大家好,我是大D。今天繼續瞭解下 HBase 是如何寫入數據的,然後再講解一下一個比較經典的面試題。 ...
  • 本文介紹如何利用 SQL UNION 操作符將多條 SELECT 語句組合成一個結果集。使用 UNION 可極大地簡化複雜的 WHERE 子句,簡化從多個表中檢索數據的工作。 一、組合查詢 多數 SQL 查詢只包含從一個或多個表中返回數據的單條 SELECT 語句。但是,SQL 也允許執行多個查詢( ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...