主頁 > 資料庫 > SQL JOIN 以列為單位對表進行聯結

SQL JOIN 以列為單位對表進行聯結

2021-12-07 08:04:02 資料庫

目錄
  • 一、什么是聯結
  • 二、行內結——INNER JOIN
    • 2.1 行內結要點 ① ——FROM 子句
    • 2.2 行內結要點 ②—— ON 子句
    • 2.3 行內結要點 ③ ——SELECT 子句
      • 2.3.1 行內結和 WHERE 子句結合使用
  • 三、外聯結——OUTER JOIN
    • 3.1 外聯結要點 ① ——選取出單張表中全部的資訊
    • 3.2 外聯結要點 ② ——每張表都是主表嗎?
  • 四、3 張以上的表的聯結
  • 五、交叉聯結——CROSS JOIN
  • 六、聯結的特定語法和過時語法
  • 請參閱

學習重點

  • 聯結(JOIN)就是將其他表中的列添加過來,進行“添加列”的集合運算,UNION 是以行(縱向)為單位進行操作,而聯結則是以列(橫向)為單位進行的,

  • 聯結大體上分為行內結和外聯結兩種,首先請大家牢牢掌握這兩種聯結的使用方法,

  • 請大家一定要使用標準 SQL 的語法格式來寫聯結運算,對于那些過時的或者特定 SQL 中的寫法,了解一下即可,不建議使用,

一、什么是聯結

在 表的加減法 中我們學習了 UNIONINTERSECT 等集合運算,這些集合運算的特征就是以行方向為單位進行操作,通俗地說,就是進行這些集合運算時,會導致記錄行數的增減,使用 UNION 會增加記錄行數,而使用 INTERSECT 或者 EXCEPT 會減少記錄行數 [1]

但是這些運算不會導致列數的改變,作為集合運算物件的表的前提就是列數要一致,因此,運算結果不會導致列的增減,

本文將要學習的 聯結(JOIN 運算,簡單來說,就是將其他表中的列添加過來,進行“添加列”的運算(圖 5),該操作通常用于無法從一張表中獲取期望資料(列)的情況,截至目前,本教程中出現的示例基本上都是從一張表中選取資料,但實際上,期望得到的資料往往會分散在不同的表之中,使用聯結就可以從多張表(3 張以上的表也沒關系)中選取資料了,

KEYWORD

  • 聯結(JOIN
聯結的圖示

圖 5 聯結的圖示

SQL 的聯結根據其用途可以分為很多種類,這里希望大家掌握的有兩種,行內結和外聯結,接下來,我們就以這兩種聯結為中心進行學習,

二、行內結——INNER JOIN

首先我們來學習行內結(INNER JOIN,它是應用最廣泛的聯結運算,大家現在可以暫時忽略“內”這個字,之后會給大家詳細說明,

KEYWORD

  • 行內結(INNER JOIN

本例中我們會繼續使用 Product 表和 謂詞 創建的 ShopProduct 表,下面我們再來回顧一下這兩張表的內容,

表 7-1 Product(商品)表

商品編號 商品名稱 商品種類 銷售單價 進貨單價 登記日期
0001 T 恤衫 衣服 1000 500 2009-09-20
0002 打孔器 辦公用品 500 320 2009-09-11
0003 運動 T 恤 衣服 4000 2800
0004 菜刀 廚房用具 3000 2800 2009-09-20
0005 高壓鍋 廚房用具 6800 5000 2009-01-15
0006 叉子 廚房用具 500 2009-09-20
0007 擦菜板 廚房用具 880 790 2008-04-28
0008 圓珠筆 辦公用品 100 2009-11-11

表 2 ShopProduct(商店商品)表

shop_id
(商店)
shop_name
(商店名稱)
product_id
(商品編號)
quantity
(數量)
000A 東京 0001 30
000A 東京 0002 50
000A 東京 0003 15
000B 名古屋 0002 30
000B 名古屋 0003 120
000B 名古屋 0004 20
000B 名古屋 0006 10
000B 名古屋 0007 40
000C 大阪 0003 20
000C 大阪 0004 50
000C 大阪 0006 90
000C 大阪 0007 70
000D 福岡 0001 100

對這兩張表包含的列進行整理后的結果如表 3 所示,

表 3 兩張表及其包含的列

-- Product ShopProduct
商品編號
商品名稱
商品種類
銷售單價
進貨單價
登記日期
商店編號
商店名稱
數量

如上表所示,兩張表中的列可以分為如下兩類,

A:兩張表中都包含的列 → 商品編號

B:只存在于一張表內的列 → 商品編號之外的列

所謂聯結運算,一言以蔽之,就是“以 A 中的列作為橋梁,將 B 中滿足同樣條件的列匯集到同一結果之中”,具體程序如下所述,

ShopProduct 表中的資料我們能夠知道,東京店(000A)銷售商品編號為 0001、0002 和 0003 的商品,但這些商品的商品名稱(product_name)和銷售單價(sale_price)在 ShopProduct 表中并不存在,這些資訊都保存在 Product 表中,大阪店和名古屋店的情況也是如此,

下面我們就試著從 Product 表中取出商品名稱(product_name)和銷售單價(sale_price),并與 ShopProduct 表中的內容進行結合,所得到的結果如下所示,

執行結果

 shop_id  | shop_name | product_id  | product_name | sale_price
----------+-----------+-------------+--------------+-------------
 000A     | 東京      | 0002        | 打孔器       |        500
 000A     | 東京      | 0003        | 運動T恤      |       4000
 000A     | 東京      | 0001        | T恤衫        |       1000
 000B     | 名古屋    | 0007        | 擦菜板       |        880
 000B     | 名古屋    | 0002        | 打孔器       |        500
 000B     | 名古屋    | 0003        | 運動T恤      |       4000
 000B     | 名古屋    | 0004        | 菜刀         |       3000
 000B     | 名古屋    | 0006        | 叉子         |        500
 000C     | 大阪      | 0007        | 擦菜板       |        880
 000C     | 大阪      | 0006        | 叉子         |        500
 000C     | 大阪      | 0003        | 運動T恤      |       4000
 000C     | 大阪      | 0004        | 菜刀         |       3000
 000D     | 福岡      | 0001        | T恤衫        |       1000

能夠得到上述結果的 SELECT 陳述句如代碼清單 9 所示,

代碼清單 9 將兩張表進行行內結

SQL Server DB2 PostgreSQL MySQL

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
  FROM ShopProduct AS SP INNER JOIN Product AS P -----①
    ON SP.product_id = P.product_id;

特定的 SQL

在 Oracle 的 FROM 子句中不能使用 AS(會發生錯誤),因此,在 Oracle 中執行代碼清單 9 時,請將 ① 的部分變為“FROM ShopProduct SP INNER JOIN Product P”,

關于行內結,請大家注意以下三點,

2.1 行內結要點 ① ——FROM 子句

第一點要注意的是,之前的 FROM 子句中只有一張表,而這次我們同時使用了 ShopProductProduct 兩張表,

FROM ShopProduct AS SP INNER JOIN Product AS P

使用關鍵字 INNER JOIN 就可以將兩張表聯結在一起了,SPP 分別是這兩張表的別名,但別名并不是必需的,在 SELECT 子句中直接使用 ShopProductproduct_id 這樣的表的原名也沒有關系,但由于表名太長會影響 SQL 陳述句的可讀性,因此還是希望大家能夠習慣使用別名 [2]

法則 3

進行聯結時需要在 FROM 子句中使用多張表,

2.2 行內結要點 ②—— ON 子句

第二點要注意的是 ON 后面的聯結條件,

KEYWORD

  • ON 子句
ON SP.product_id = P.product_id

我們可以在 ON 之后指定兩張表聯結所使用的列(聯結鍵),本例中使用的是商品編號(product_id),也就是說,ON 是專門用來指定聯結條件的,它能起到與 WHERE 相同的作用,需要指定多個鍵時,同樣可以使用 ANDOR,在進行行內結時 ON 子句是必不可少的(如果沒有 ON 會發生錯誤),并且 ON 必須書寫在 FROMWHERE 之間,

KEYWORD

  • 聯結鍵

法則 4

進行行內結時必須使用 ON 子句,并且要書寫在 FROMWHERE 之間,

舉個比較直觀的例子,ON 就像是連接河流兩岸城鎮的橋梁一樣(圖 6),

使用 ON 進行兩表加法運算(和集)的圖示

圖 6 使用 ON 進行兩表加法運算(和集)的圖示

聯結條件也可以使用“=”來記述,在語法上,還可以使用 <=BETWEEN 等謂詞,但因為實際應用中九成以上都可以用“=”進行聯結,所以開始時大家只要記住使用“=”就可以了,使用“=”將聯結鍵關聯起來,就能夠將兩張表中滿足相同條件的記錄進行“聯結”了,

2.3 行內結要點 ③ ——SELECT 子句

第三點要注意的是,在 SELECT 子句中指定的列,

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price

SELECT 子句中,像 SP.shop_idP.sale_price 這樣使用“<表的別名>.<列名>”的形式來指定列,和使用一張表時不同,由于多表聯結時,某個列到除錯于哪張表比較容易混亂,因此采用了這樣的防范措施,從語法上來說,只有那些同時存在于兩張表中的列(這里是 product_id)必須使用這樣的書寫方式,其他的列像 shop_id 這樣直接書寫列名也不會發生錯誤,但是就像前面說的那樣,為了避免混亂,還是希望大家能夠在使用聯結時按照“<表的別名>.<列名>”的格式來書寫 SELECT 子句中全部的列,

法則 5

使用聯結時 SELECT 子句中的列需要按照“<表的別名>.<列名>”的格式進行書寫,

2.3.1 行內結和 WHERE 子句結合使用

如果并不想了解所有商店的情況,例如只想知道東京店(000A)的資訊時,可以像之前學習的那樣在 WHERE 子句中添加條件,這樣我們就可以從代碼清單 9 中得到的全部商店的資訊中選取出東京店的記錄了,

代碼清單 10 行內結和 WHERE 子句結合使用

SQL Server DB2 PostgreSQL MySQL

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
  FROM ShopProduct AS SP INNER JOIN Product AS P ----①
    ON SP.product_id = P.product_id
 WHERE SP.shop_id = '000A';

特定的 SQL

在 Oracle 中執行代碼清單 10 時,請將 ① 的部分變為“FROM ShopProduct SP INNER JOIN Product P”(刪掉 FROM 子句中的 AS),

執行結果

 shop_id | shop_name | product_id | product_name | sale_price
---------+-----------+------------+--------------+-----------
 000A    | 東京      | 0001       | T恤衫         |      1000
 000A    | 東京      | 0002       | 打孔器        |       500
 000A    | 東京      | 0003       | 運動T恤       |      4000

像這樣使用聯結運算將滿足相同規則的表聯結起來時,WHEREGROUP BYHAVINGORDER BY 等工具都可以正常使用,我們可以將聯結之后的結果想象為新創建出來的一張表(表 4),對這張表使用 WHERE 子句等工具,這樣理解起來就容易多了吧,

當然,這張“表”只在 SELECT 陳述句執行期間存在,SELECT 陳述句執行之后就會消失,如果希望繼續使用這張“表”,還是將它創建成視圖吧,

表 4 通過聯結創建出的表(ProductJoinShopProduct)的圖示

shop_id
(編號)
shop_name
(商品名稱)
product_id
(商品編號)
product_name
(商品名稱)
sale_price
(銷售單價)
000A 東京 0001 T 恤衫 1000
000A 東京 0002 打孔器 500
000A 東京 0003 運動 T 恤 4000
000B 名古屋 0002 打孔器 500
000B 名古屋 0003 運動 T 恤 4000
000B 名古屋 0004 菜刀 3000
000B 名古屋 0006 叉子 500
000B 名古屋 0007 擦菜板 880
000C 大阪 0003 運動 T 恤 4000
000C 大阪 0004 菜刀 3000
000C 大阪 0006 叉子 500
000C 大阪 0007 擦菜板 880
000D 福岡 0001 T 恤衫 1000

三、外聯結——OUTER JOIN

行內結之外比較重要的就是 外聯結(OUTER JOIN 了,我們再來回顧一下前面的例子,在前例中,我們將 Product 表和 ShopProduct 表進行行內結,從兩張表中取出各個商店銷售的商品資訊,其中,實作“從兩張表中取出”的就是聯結功能,

KEYWORD

  • 外聯結(OUTER JOIN

外聯結也是通過 ON 子句的聯結鍵將兩張表進行聯結,并從兩張表中同時選取相應的列的,基本的使用方法并沒有什么不同,只是結果卻有所不同,事實勝于雄辯,還是讓我們先把之前行內結的 SELECT 陳述句(代碼清單 9)轉換為外聯結試試看吧,轉換的結果請參考代碼清單 11,

代碼清單 11 將兩張表進行外聯結

SQL Server DB2 PostgreSQL MySQL

SELECT SP.shop_id, SP.shop_name, P.product_id, P.product_name, P.sale_price
  FROM ShopProduct AS SP RIGHT OUTER JOIN Product AS P ①
    ON SP.product_id = P.product_id;

特定的 SQL

在 Oracle 中執行代碼清單 11 時,請將 ① 的部分變為“FROM ShopProduct SP RIGHT OUTER JOIN Product P”(洗掉掉 FROM 子句中的 AS),

執行結果

執行結果

3.1 外聯結要點 ① ——選取出單張表中全部的資訊

與行內結的結果相比,不同點顯而易見,那就是結果的行數不一樣,行內結的結果中有 13 條記錄,而外聯結的結果中有 15 條記錄,增加的 2 條記錄到底是什么呢?

這正是外聯結的關鍵點,多出的 2 條記錄是高壓鍋和圓珠筆,這 2 條記錄在 ShopProduct 表中并不存在,也就是說,這 2 種商品在任何商店中都沒有銷售,由于行內結只能選取出同時存在于兩張表中的資料,因此只在 Product 表中存在的 2 種商品并沒有出現在結果之中,

相反,對于外聯結來說,只要資料存在于某一張表當中,就能夠讀取出來,在實際的業務中,例如想要生成固定行數的單據時,就需要使用外聯結,如果使用行內結的話,根據 SELECT 陳述句執行時商店庫存狀況的不同,結果的行數也會發生改變,生成的單據的版式也會受到影響,而使用外聯結能夠得到固定行數的結果,

雖說如此,那些表中不存在的資訊我們還是無法得到,結果中高壓鍋和圓珠筆的商店編號和商店名稱都是 NULL(具體資訊大家都不知道,真是無可奈何),外聯結名稱的由來也跟 NULL 有關,即“結果中包含原表中不存在(在原表之外)的資訊”,相反,只包含表內資訊的聯結也就被稱為行內結了,

3.2 外聯結要點 ② ——每張表都是主表嗎?

外聯結還有一點非常重要,那就是要把哪張表作為主表,最終的結果中會包含主表內所有的資料,指定主表的關鍵字是 LEFTRIGHT,顧名思義,使用 LEFTFROM 子句中寫在左側的表是主表,使用 RIGHT 時右側的表是主表,代碼清單 11 中使用了 RIGHT,因此,右側的表,也就是 Product 表是主表,

KEYWORD

  • LEFT 關鍵字

  • RIGHT 關鍵字

我們還可以像代碼清單 12 這樣進行改寫,意思完全相同,

代碼清單 12 改寫后外聯結的結果完全相同

SQL Server DB2 PostgreSQL MySQL

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
  FROM Product AS P LEFT OUTER JOIN ShopProduct AS SP ①
    ON SP.product_id = P.product_id;

特定的 SQL

在 Oracle 中執行代碼清單 12 時,請將 ① 的部分變為“FROM ShopProduct SP LEFT OUTER JOIN Product P”(洗掉掉 FROM 子句中的 AS),

大家可能會猶豫到底應該使用 LEFT 還是 RIGHT,其實它們的功能沒有任何區別,使用哪一個都可以,通常使用 LEFT 的情況會多一些,但也并沒有非使用這個不可的理由,使用 RIGHT 也沒有問題,

法則 6

外聯結中使用 LEFTRIGHT來指定主表,使用二者所得到的結果完全相同,

四、3 張以上的表的聯結

通常聯結只涉及 2 張表,但有時也會出現必須同時聯結 3 張以上的表的情況,原則上聯結表的數量并沒有限制,下面就讓我們來看一下 3 張表的聯結吧,

首先我們創建一張用來管理庫存商品的表(表 5),假設商品都保存在 P001 和 P002 這 2 個倉庫之中,

表 5 InventoryProduct(庫存商品)表

inventory_id
(倉庫編號)
product_id
(商品編號)
inventory_quantity
(庫存數量)
P001 0001 0
P001 0002 120
P001 0003 200
P001 0004 3
P001 0005 0
P001 0006 99
P001 0007 999
P001 0008 200
P002 0001 10
P002 0002 25
P002 0003 34
P002 0004 19
P002 0005 99
P002 0006 0
P002 0007 0
P002 0008 18

創建該表及插入資料的 SQL 陳述句請參考代碼清單 13,

代碼清單 13 創建 InventoryProduct 表并向其中插入資料

-- DDL :創建表
CREATE TABLE InventoryProduct
( inventory_id        CHAR(4)  NOT NULL,
  product_id          CHAR(4)  NOT NULL,
  inventory_quantity INTEGER   NOT NULL,
  PRIMARY KEY (inventory_id, product_id));

SQL Server PostgreSQL

-- DML :插入資料
BEGIN TRANSACTION; ------------①

INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0001',   0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0002',   120);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0003',   200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0004',   3);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0005',   0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0006',   99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0007',   999);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001',      '0008',   200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0001',   10);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0002',   25);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0003',   34);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0004',   19);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0005',   99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0006',   0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0007',   0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002',      '0008',   18);

COMMIT;

特定的 SQL

不同的 DBMS 的事務處理的語法也不盡相同,代碼清單 13 中的 DML 陳述句在 MySQL 中執行時,需要將 ① 部分更改為“START TRANSACTION;”,在 Oracle 和 DB2 中執行時,無需用到 ① 的部分(請洗掉),

詳細內容請大家參考 事務 中的“創建事務”,

下面我們從上表中取出保存在 P001 倉庫中的商品數量,并將該列添加到代碼清單 11 所得到的結果中,聯結方式為行內結(外聯結的使用方法完全相同),聯結鍵為商品編號(product_id)(代碼清單 14),

代碼清單 14 對 3 張表進行行內結

對 3 張表進行行內結

特定的 SQL

在 Oracle 中執行代碼清單 14 時,請將 ① 的部分變為“FROM ShopProduct SP INNER JOIN Product P”,將 ② 的部分變為“INNER JOIN InventoryProduct IP”(洗掉掉 FROM 子句中的 AS

執行結果

 shop_id | shop_name | product_id | product_name | sale_price | inventory_quantity
---------+-----------+------------+--------------+------------+-------------------
 000A    | 東京      | 0002       | 打孔器       |        500 |                120
 000A    | 東京      | 0003       | 運動T恤      |       4000 |                200
 000A    | 東京      | 0001       | T恤衫        |       1000 |                  0
 000B    | 名古屋    | 0007       | 擦菜板       |        880 |                999
 000B    | 名古屋    | 0002       | 打孔器       |        500 |                120
 000B    | 名古屋    | 0003       | 運動T恤      |       4000 |                200
 000B    | 名古屋    | 0004       | 菜刀         |       3000 |                  3
 000B    | 名古屋    | 0006       | 叉子         |        500 |                 99
 000C    | 大阪      | 0007       | 擦菜板       |        880 |                999
 000C    | 大阪      | 0006       | 叉子         |        500 |                 99
 000C    | 大阪      | 0003       | 運動T恤      |       4000 |                200
 000C    | 大阪      | 0004       | 菜刀         |       3000 |                  3
 000D    | 福岡      | 0001       | T恤衫        |       1000 |                  0

在代碼清單 11 行內結的 FROM 子句中,再次使用 INNER JOINInventoryProduct 表也添加了進來,

FROM ShopProduct AS SP INNER JOIN Product AS P
  ON SP.product_id = P.product_id
        INNER JOIN InventoryProduct AS IP
           ON SP.product_id = IP.product_id

通過 ON 子句指定聯結條件的方式也沒有發生改變,使用等號將作為聯結條件的 Product 表和 ShopProduct 表中的商品編號(product_id)聯結起來,由于 Product 表和 ShopProduct 表已經進行了聯結,因此這里無需再對 Product 表和 InventoryProduct 表進行聯結了(雖然也可以進行聯結,但結果并不會發生改變),

即使想要把聯結的表增加到 4 張、5 張……使用 INNER JOIN 進行添加的方式也是完全相同的,

五、交叉聯結——CROSS JOIN

接下來和大家一起學習第 3 種聯結方式——交叉聯結(CROSS JOIN,其實這種聯結在實際業務中并不會使用(筆者使用這種聯結的次數也屈指可數),那為什么還要在這里進行介紹呢?這是因為交叉聯結是所有聯結運算的基礎,

KEYWORD

  • 交叉聯結(CROSS JOIN

交叉聯結本身非常簡單,但是其結果有點麻煩,下面我們就試著將 Product 表和 ShopProduct 表進行交叉聯結(代碼清單 15),

代碼清單 15 將兩張表進行交叉聯結

SQL Server DB2 PostgreSQL MySQL

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name
  FROM ShopProduct AS SP CROSS JOIN Product AS P; -----①

特定的 SQL

在 Oracle 中執行代碼清單 15 時,請將 ① 的部分變為“FROM ShopProduct SP CROSS JOIN Product P;”(洗掉掉 FROM 子句中的 AS),

執行結果

 shop_id | shop_name | product_id | product_name
---------+-----------+------------+-------------
 000A    | 東京      | 0001       | T恤衫
 000A    | 東京      | 0002       | T恤衫
 000A    | 東京      | 0003       | T恤衫
 000B    | 名古屋    | 0002       | T恤衫
 000B    | 名古屋    | 0003       | T恤衫
 000B    | 名古屋    | 0004       | T恤衫
 000B    | 名古屋    | 0006       | T恤衫
 000B    | 名古屋    | 0007       | T恤衫
 000C    | 大阪      | 0003       | T恤衫
 000C    | 大阪      | 0004       | T恤衫
 000C    | 大阪      | 0006       | T恤衫
 000C    | 大阪      | 0007       | T恤衫
 000D    | 福岡      | 0001       | T恤衫
 000A    | 東京      | 0001       | 打孔器
 000A    | 東京      | 0002       | 打孔器
 000A    | 東京      | 0003       | 打孔器
 000B    | 名古屋    | 0002       | 打孔器
 000B    | 名古屋    | 0003       | 打孔器
 000B    | 名古屋    | 0004       | 打孔器
 000B    | 名古屋    | 0006       | 打孔器
 000B    | 名古屋    | 0007       | 打孔器
 000C    | 大阪      | 0003       | 打孔器
 000C    | 大阪      | 0004       | 打孔器
 000C    | 大阪      | 0006       | 打孔器
 000C    | 大阪      | 0007       | 打孔器
 000D    | 福岡      | 0001       | 打孔器
 000A    | 東京      | 0001       | 運動T恤
 000A    | 東京      | 0002       | 運動T恤
 000A    | 東京      | 0003       | 運動T恤
 000B    | 名古屋    | 0002       | 運動T恤
 000B    | 名古屋    | 0003       | 運動T恤
 000B    | 名古屋    | 0004       | 運動T恤
 000B    | 名古屋    | 0006       | 運動T恤
 000B    | 名古屋    | 0007       | 運動T恤
 000C    | 大阪      | 0003       | 運動T恤
 000C    | 大阪      | 0004       | 運動T恤
 000C    | 大阪      | 0006       | 運動T恤
 000C    | 大阪      | 0007       | 運動T恤
 000D    | 福岡      | 0001       | 運動T恤
 000A    | 東京      | 0001       | 菜刀
 000A    | 東京      | 0002       | 菜刀
 000A    | 東京      | 0003       | 菜刀
 000B    | 名古屋    | 0002       | 菜刀
 000B    | 名古屋    | 0003       | 菜刀
 000B    | 名古屋    | 0004       | 菜刀
 000B    | 名古屋    | 0006       | 菜刀
 000B    | 名古屋    | 0007       | 菜刀
 000C    | 大阪      | 0003       | 菜刀
 000C    | 大阪      | 0004       | 菜刀
 000C    | 大阪      | 0006       | 菜刀
 000C    | 大阪      | 0007       | 菜刀
 000D    | 福岡      | 0001       | 菜刀
 000A    | 東京      | 0001       | 高壓鍋
 000A    | 東京      | 0002       | 高壓鍋
 000A    | 東京      | 0003       | 高壓鍋
 000B    | 名古屋    | 0002       | 高壓鍋
 000B    | 名古屋    | 0003       | 高壓鍋
 000B    | 名古屋    | 0004       | 高壓鍋
 000B    | 名古屋    | 0006       | 高壓鍋
 000B    | 名古屋    | 0007       | 高壓鍋
 000C    | 大阪      | 0003       | 高壓鍋
 000C    | 大阪      | 0004       | 高壓鍋
 000C    | 大阪      | 0006       | 高壓鍋
 000C    | 大阪      | 0007       | 高壓鍋
 000D    | 福岡      | 0001       | 高壓鍋
 000A    | 東京      | 0001       | 叉子
 000A    | 東京      | 0002       | 叉子
 000A    | 東京      | 0003       | 叉子
 000B    | 名古屋    | 0002       | 叉子
 000B    | 名古屋    | 0003       | 叉子
 000B    | 名古屋    | 0004       | 叉子
 000B    | 名古屋    | 0006       | 叉子
 000B    | 名古屋    | 0007       | 叉子
 000C    | 大阪      | 0003       | 叉子
 000C    | 大阪      | 0004       | 叉子
 000C    | 大阪      | 0006       | 叉子
 000C    | 大阪      | 0007       | 叉子
 000D    | 福岡      | 0001       | 叉子
 000A    | 東京      | 0001       | 擦菜板
 000A    | 東京      | 0002       | 擦菜板
 000A    | 東京      | 0003       | 擦菜板
 000B    | 名古屋    | 0002       | 擦菜板
 000B    | 名古屋    | 0003       | 擦菜板
 000B    | 名古屋    | 0004       | 擦菜板
 000B    | 名古屋    | 0006       | 擦菜板
 000B    | 名古屋    | 0007       | 擦菜板
 000C    | 大阪      | 0003       | 擦菜板
 000C    | 大阪      | 0004       | 擦菜板
 000C    | 大阪      | 0006       | 擦菜板
 000C    | 大阪      | 0007       | 擦菜板
 000D    | 福岡      | 0001       | 擦菜板
 000A    | 東京      | 0001       | 圓珠筆
 000A    | 東京      | 0002       | 圓珠筆
 000A    | 東京      | 0003       | 圓珠筆
 000B    | 名古屋    | 0002       | 圓珠筆
 000B    | 名古屋    | 0003       | 圓珠筆
 000B    | 名古屋    | 0004       | 圓珠筆
 000B    | 名古屋    | 0006       | 圓珠筆
 000B    | 名古屋    | 0007       | 圓珠筆
 000C    | 大阪      | 0003       | 圓珠筆
 000C    | 大阪      | 0004       | 圓珠筆
 000C    | 大阪      | 0006       | 圓珠筆
 000C    | 大阪      | 0007       | 圓珠筆
 000D    | 福岡      | 0001       | 圓珠筆

可能大家會驚訝于結果的行數,但我們還是先來介紹一下語法結構吧,對滿足相同規則的表進行交叉聯結的集合運算子是 CROSS JOIN(笛卡兒積),進行交叉聯結時無法使用行內結和外聯結中所使用的 ON 子句,這是因為交叉聯結是對兩張表中的全部記錄進行交叉組合,因此結果中的記錄數通常是兩張表中行數的乘積,本例中,因為 ShopProduct 表存在 13 條記錄,Product 表存在 8 條記錄,所以結果中就包含了 13 × 8 = 104 條記錄,

KEYWORD

  • CROSS JOIN(笛卡兒積)

可能這時會有讀者想起在 表的加減法 中我們提到過集合運算中的乘法會在本節中進行詳細學習,這就是上面介紹的交叉聯結,

行內結是交叉聯結的一部分,“內”也可以理解為“包含在交叉聯結結果中的部分”,相反,外聯結的“外”可以理解為“交叉聯結結果之外的部分”,

交叉聯結沒有應用到實際業務之中的原因有兩個,一是其結果沒有實用價值,二是由于其結果行數太多,需要花費大量的運算時間和高性能設備的支持,

六、聯結的特定語法和過時語法

之前我們學習的行內結和外聯結的語法都符合標準 SQL 的規定,可以在所有 DBMS 中執行,因此大家可以放心使用,但是如果大家之后從事系統開發作業的話,一定會碰到需要閱讀他人寫的代碼并進行維護的情況,而那些使用特定和過時語法的程式就會成為我們的麻煩,

SQL 是一門特定語法及過時語法非常多的語言,雖然之前本教程中也多次提及,但聯結是其中特定語法的部分,現在還有不少年長的程式員和系統工程師仍在使用這些特定的語法,

例如,將本文最初介紹的行內結的 SELECT 陳述句(代碼清單 9)替換為過時語法的結果如下所示(代碼清單 16),

代碼清單 16 使用過時語法的行內結(結果與代碼清單 9 相同)

SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
  FROM ShopProduct SP, Product P
 WHERE SP.product_id = P.product_id
   AND SP.shop_id = '000A';

這樣的書寫方式所得到的結果與標準語法完全相同,并且這樣的語法可以在所有的 DBMS 中執行,并不能算是特定的語法,只是過時了而已,

但是,由于這樣的語法不僅過時,而且還存在很多其他的問題,因此不推薦大家使用,理由主要有以下三點,

第一,使用這樣的語法無法馬上判斷出到底是行內結還是外聯結(又或者是其他種類的聯結),

第二,由于聯結條件都寫在 WHERE 子句之中,因此無法在短時間內分辨出哪部分是聯結條件,哪部分是用來選取記錄的限制條件,

第三,我們不知道這樣的語法到底還能使用多久,每個 DBMS 的開發者都會考慮放棄過時的語法,轉而支持新的語法,雖然并不是馬上就不能使用了,但那一天總會到來的,

雖然這么說,但是現在使用這些過時語法撰寫的程式還有很多,到目前為止還都能正常執行,我想大家很可能會碰到這樣的代碼,因此還是希望大家能夠了解這些知識,

法則 7

對于聯結的過時語法和特定語法,雖然不建議使用,但還是希望大家能夠讀懂,

專欄

關系除法

這個部分的教程中我們學習了以下 4 個集合運算子,

  • UNION(并集)

  • EXCEPT(差集)

  • INTERSECT(交集)

  • CROSS JOIN(笛卡兒積)

雖然交集是一種獨立的集合運算,但實際上它也是“只包含公共部分的特殊 UNION”,剩下的 3 個在四則運算中也有對應的運算,但是,除法運算還沒有介紹,

難道集合運算中沒有除法嗎?當然不是,除法運算是存在的,集合運算中的除法通常稱為關系除法,關系是數學領域中對表或者視圖的稱謂,但是并沒有定義像 UNION 或者 EXCEPT 這樣專用的運算子,如果要定義,估計應該是 DIVIDE(除)吧,但截至目前并沒有 DBMS 使用這樣的運算子,

KEYWORD

  • 關系除法

為什么只有除法運算不使用運算子(只有除法)對被除數進行運算呢?其中的理由有點復雜,還是讓我們先來介紹一下“表的除法”具體是一種什么樣的運算吧,

我們使用表 A 和表 B 兩張表作為示例用表,

表 A Skills(技術)表:關系除法中的除數

skill --
Oracle
UNIX
Java

表 B EmpSkills(員工技術)表:關系除法中的被除數

emp skill
相田 Oracle
相田 UNIX
相田 Java
相田 C#
神崎 Oracle
神崎 UNIX
神崎 Java
平井 UNIX
平井 Oracle
平井 PHP
平井 Perl
平井 C++
若田部 Perl
渡來 Oracle

創建上述兩張表并向其中插入資料的 SQL 陳述句請參考代碼清單 A,

代碼清單 A 創建 Skills/EmpSkills表并插入資料

-- DDL :創建表
CREATE TABLE Skills
(skill VARCHAR(32),
PRIMARY KEY(skill));

CREATE TABLE EmpSkills
(emp   VARCHAR(32),
skill VARCHAR(32),
PRIMARY KEY(emp, skill));

SQL Server PostgreSQL

-- DML :插入資料
BEGIN TRANSACTION; -------------①

INSERT INTO Skills VALUES('Oracle');
INSERT INTO Skills VALUES('UNIX');
INSERT INTO Skills VALUES('Java');

INSERT INTO EmpSkills VALUES('相田', 'Oracle');
INSERT INTO EmpSkills VALUES('相田', 'UNIX');
INSERT INTO EmpSkills VALUES('相田', 'Java');
INSERT INTO EmpSkills VALUES('相田', 'C#');
INSERT INTO EmpSkills VALUES('神崎', 'Oracle');
INSERT INTO EmpSkills VALUES('神崎', 'UNIX');
INSERT INTO EmpSkills VALUES('神崎', 'Java');
INSERT INTO EmpSkills VALUES('平井', 'UNIX');
INSERT INTO EmpSkills VALUES('平井', 'Oracle');
INSERT INTO EmpSkills VALUES('平井', 'PHP');
INSERT INTO EmpSkills VALUES('平井', 'Perl');
INSERT INTO EmpSkills VALUES('平井', 'C++');
INSERT INTO EmpSkills VALUES('若田部', 'Perl');
INSERT INTO EmpSkills VALUES('渡來', 'Oracle');

COMMIT;

特定的 SQL

不同的 DBMS 的事務處理的語法也不盡相同,代碼清單 A 中的 DML 陳述句在 MySQL 中執行時,需要將 ① 部分更改為“START TRANSACTION;”,在 Oracle 和 DB2 中執行時,無需用到 ① 的部分(請洗掉),

詳細內容請大家參考 事務 中的“創建事務”,

EmpSkills 表中保存了某個系統公司員工所掌握的技術資訊,例如,從該表中我們可以了解到相田掌握了 Oracle、UNIX、Java、C# 這 4 種技術,

下面我們來思考一下如何從該表中選取出掌握了 Skills 表中所有 3 個領域的技術的員工吧(代碼清單 B),

代碼清單 B 選取出掌握所有 3 個領域的技術的員工

SELECT DISTINCT emp
 FROM EmpSkills ES1
WHERE NOT EXISTS
       (SELECT skill
          FROM Skills
        EXCEPT
        SELECT skill
          FROM EmpSkills ES2
         WHERE ES1.emp = ES2.emp);

這樣我們就得到了包含相田和神崎 2 人的結果,雖然平井也掌握了 Orcale 和 UNIX,但很可惜他不會使用 Java,因此沒有選取出來,

執行結果(關系除法中的商)

emp
------
神崎
相田

這樣的結果滿足了除法運算的基本規則,肯定有讀者會產生這樣的疑問:“到底上述運算中什么地方是除法運算呢?”實際上這和數值的除法既相似又有所不同,大家從與除法相對的乘法運算的角度去思考就能得到答案了,

除法和乘法是相輔相成的關系,除法運算的結果(商)乘以除數就能得到除法運算前的被除數了,例如對于 20÷4 = 5 來說,就是 5(商)×4(除數) = 20( 被除數)(圖 A),

除法運算和乘法運算相輔相成的關系圖

圖 A 除法運算和乘法運算相輔相成的關系圖

關系除法中這樣的規則也是成立的,通過商和除數相乘,也就是交叉聯結,就能夠得到作為被除數的集合了 [3]

如上所述,除法運算是集合運算中最復雜的運算,但是其在實際業務中的應用十分廣泛,因此希望大家能在達到中級以上水平時掌握其使用方法,此外,想要詳細了解 SQL 中除法運算實作方法的讀者,可以參考《SQL進階教程》中的 1-4 節和 1-7 節,

請參閱

  • 表的加減法
  • SQL 聯結

(完)


  1. 根據表中資料的不同,也存在行數不發生變化的情況, ??

  2. FROM 子句中使用表的別名時,像 Product AS P 這樣使用 AS 是標準 SQL 正式的語法,但是在 Oracle 中使用 AS 會發生錯誤,因此,在 Oracle 中使用時,需要注意不要在 FROM 子句中使用 AS, ??

  3. 雖然不能恢復成完整的被除數,但是這里我們也不再追究了, ??

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/374446.html

標籤:SQL Server

上一篇:SQL UNION(并集)、INTERSECT(交集)、EXCEPT(差集)

下一篇:mysql與事務、恢復相關的引數決議

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more