《SQL必知必會(huì)》筆記7-高級(jí)聯(lián)結(jié)(as、self-join、outer join)、組合查詢union

1 創(chuàng)建高級(jí)聯(lián)結(jié)(別名、自聯(lián)結(jié)、外聯(lián)結(jié))

1.1 使用表別名AS

使用別名的兩個(gè)主要理由:

  • 縮短SQL語(yǔ)句。
  • 允許在一條SELECT語(yǔ)句中多次使用相同的表。
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';

別名不僅能用于WHERE子句,還可以用于SELECT列表、ORDER BY子句以及其他語(yǔ)句部分。


1.2 使用不同類型的聯(lián)結(jié)

除了內(nèi)聯(lián)結(jié)INNER JOIN之外,還有三種其他聯(lián)結(jié)方式:自聯(lián)結(jié)(self-join)、自然聯(lián)結(jié)(natural join)和外聯(lián)結(jié)(outer join)。


1.2.1 自聯(lián)結(jié)self-join

自聯(lián)結(jié)指的是要檢索的兩個(gè)表實(shí)際上是同一個(gè)表。

假如要給與Jim Jones同一公司的所有顧客發(fā)送一份郵件。

方法一:子查詢

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

方法二:自聯(lián)結(jié)查詢

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

一般來(lái)說(shuō),許多DBMS處理聯(lián)結(jié)遠(yuǎn)比處理子查詢快得多。


1.2.2 自然聯(lián)結(jié)natural join

在對(duì)表進(jìn)行聯(lián)結(jié)時(shí),至少有一列(被聯(lián)結(jié)的列)不止出現(xiàn)在一個(gè)表中。標(biāo)準(zhǔn)的內(nèi)聯(lián)結(jié)返回所有數(shù)據(jù),相同的列甚至多次出現(xiàn)。自然聯(lián)結(jié)排除多次出現(xiàn),使每一列只返回一次。

自然聯(lián)結(jié)要求只能選擇那些唯一的列,一般通過(guò)對(duì)一個(gè)表使用通配符(SELECT *),而對(duì)其他表的列使用明確的子集來(lái)完成。

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

在這個(gè)例子中,通配符只對(duì)第一個(gè)表使用。所有其他列明確列出,所以沒(méi)有重復(fù)的列被檢索出來(lái)。

事實(shí)上,我們迄今為止建立的每個(gè)內(nèi)聯(lián)結(jié)都是自然聯(lián)結(jié),很可能永遠(yuǎn)都不會(huì)用到不是自然聯(lián)結(jié)的內(nèi)聯(lián)結(jié)。


1.2.3 外聯(lián)結(jié)outer join

許多聯(lián)結(jié)將一個(gè)表中的行與另一個(gè)表中的行相關(guān)聯(lián),但有時(shí)候需要包含沒(méi)有關(guān)聯(lián)的那些行。

例如:

  • 對(duì)每個(gè)顧客下的訂單進(jìn)行計(jì)數(shù),包括哪些至今尚未下訂單的顧客。
  • 列出所有產(chǎn)品以及訂購(gòu)數(shù)量,包括沒(méi)有人訂購(gòu)的產(chǎn)品。
  • 計(jì)算平均銷售規(guī)模,包括哪些至今尚未下訂單的顧客。

在上述例子中,聯(lián)結(jié)包含了那些在相關(guān)表中沒(méi)有關(guān)聯(lián)行的行,這種聯(lián)結(jié)稱為外聯(lián)結(jié)(outer join)。

內(nèi)聯(lián)結(jié)檢索所有顧客及其訂單:

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

外聯(lián)結(jié)語(yǔ)法類似,檢索包括沒(méi)有訂單顧客在內(nèi)的所有顧客:

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

使用OUTER JOIN語(yǔ)法時(shí),必須使用RIGHT或LEFT關(guān)鍵字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右邊的表,而LEFT指出的是OUTER JOIN左邊的表)。

上面的例子使用LEFT OUTER JOIN從FROM子句左邊的表(Customers表)中選擇所有行。為了從右邊的表中選擇所有行,需要使用RIGHT OUTER JOIN,如下例所示。

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

還存在另一種外聯(lián)結(jié),就是全外聯(lián)結(jié)(full outer join),它檢索兩個(gè)表中的所有行并關(guān)聯(lián)那些可以關(guān)聯(lián)的行,全外聯(lián)結(jié)包含了兩個(gè)表中的不關(guān)聯(lián)的行。(Access、MariaDB、MySQL和SQLite不支持FULL OUTER JOIN語(yǔ)法)

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

1.3 使用帶聚集函數(shù)的聯(lián)結(jié)

檢索所有顧客及每個(gè)顧客所下的訂單數(shù):

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;

聚集函數(shù)也可以方便地與其他聯(lián)結(jié)一起使用。

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;

1.4 使用聯(lián)結(jié)和聯(lián)結(jié)條件

  1. 注意所使用的聯(lián)結(jié)類型。一般我們使用內(nèi)聯(lián)結(jié),但使用外聯(lián)結(jié)也有效。
  2. 保證使用正確的聯(lián)結(jié)條件,否則會(huì)返回不正確的數(shù)據(jù)。
  3. 應(yīng)該總是提供聯(lián)結(jié)條件,否則會(huì)得出笛卡爾積。
  4. 在一個(gè)聯(lián)結(jié)中可以包含多個(gè)表,甚至可以對(duì)每個(gè)聯(lián)結(jié)采用不同的聯(lián)結(jié)類型。雖然這樣做是合法的,一般也很有用,但應(yīng)該在一起測(cè)試它們前分別測(cè)試每個(gè)聯(lián)結(jié),這會(huì)使故障排除更為簡(jiǎn)單。

2 組合查詢(UNION)

2.1 組合查詢

多數(shù)SQL查詢只包含從一個(gè)或多個(gè)表中返回?cái)?shù)據(jù)的單條SELECT語(yǔ)句,但是SQL也允許執(zhí)行多個(gè)查詢(多條SELECT語(yǔ)句),并將結(jié)果作為一個(gè)查詢結(jié)果集返回。這些組合查詢通常稱為并(union)或復(fù)合查詢(compound query)。

使用組合查詢的兩個(gè)主要情況:

  1. 在一個(gè)查詢中從不同的表返回結(jié)構(gòu)數(shù)據(jù)。
  2. 對(duì)一個(gè)表執(zhí)行多個(gè)查詢,按一個(gè)查詢返回?cái)?shù)據(jù)。

2.2 創(chuàng)建組合查詢

可用UNION操作符來(lái)組合數(shù)條SQL查詢,利用UNION,可給出多條SELECT語(yǔ)句,將它們的結(jié)果組合成一個(gè)結(jié)果集。


2.2.1 使用UNION

給出每條SELECT語(yǔ)句,在各條語(yǔ)句之間放上關(guān)鍵字UNION。

例如,需要Illinois、Indiana和Michigan等美國(guó)幾個(gè)州的所有顧客的報(bào)表,還想包括不管位于哪個(gè)州的所有的Fun4All。當(dāng)然可以利用WHERE子句來(lái)完成此工作,不過(guò)這次我們使用UNION。

使用WHERE語(yǔ)句:

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
OR cust_name = 'Fun4All';

使用UNION語(yǔ)句:

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';

在這個(gè)簡(jiǎn)單的例子中,使用UNION可能比使用WHERE子句更為復(fù)雜。但對(duì)于較復(fù)雜的過(guò)濾條件,或者從多個(gè)表(而不是一個(gè)表)中檢索數(shù)據(jù)的情形,使用UNION可能會(huì)使處理更簡(jiǎn)單。


2.2.2 UNION規(guī)則

  1. UNION必須由兩條或兩條以上的SELECT語(yǔ)句組成,語(yǔ)句之間用關(guān)鍵字UNION分隔。如果組合四條SELECT語(yǔ)句,將要使用三個(gè)UNION關(guān)鍵字。
  2. UNION中的每個(gè)查詢必須包含相同的列、表達(dá)式或聚集函數(shù),各個(gè)列不需要以相同的次序列出。
  3. 列數(shù)據(jù)類型必須兼容:類型不必完全相同,但必須是DBMS可以隱含轉(zhuǎn)換的類型(例如,不同的數(shù)值類型或不同的日期類型)。

2.2.3 包含或取消重復(fù)的行

UNION從查詢結(jié)果集中自動(dòng)去除了重復(fù)的行,它的行為與一條SELECT語(yǔ)句中使用多個(gè)WHERE子句條件一樣。使用UNION時(shí),重復(fù)的行會(huì)自動(dòng)取消。

如果想返回所有的匹配行,可使用UNION ALL而不是UNION。

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';

2.2.4 對(duì)組合查詢結(jié)果排序

在用UNION組合查詢時(shí),只能使用一條ORDER BY子句,它必須位于最后一條SELECT語(yǔ)句之后。

對(duì)于結(jié)果集,不存在用一種方式排序一部分,而又用另一種方式排序另一部分的情況,因此不允許使用多條ORDER BY子句。

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All'
ORDER BY cust_name, cust_contact;

雖然ORDER BY子句似乎只有最后一條SELECT語(yǔ)句的組成部分,但實(shí)際上DBMS將用它來(lái)排序所有SELECT語(yǔ)句返回的所有結(jié)果。


如果您發(fā)現(xiàn)文中有不清楚或者有問(wèn)題的地方,請(qǐng)?jiān)谙路皆u(píng)論區(qū)留言,我會(huì)根據(jù)您的評(píng)論,更新文中相關(guān)內(nèi)容,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • SQL與MySQL簡(jiǎn)介 數(shù)據(jù)庫(kù)基礎(chǔ) 從SQL的角度來(lái)看,數(shù)據(jù)庫(kù)就是一個(gè)以某種有組織的方式存儲(chǔ)的數(shù)據(jù)集合。我們可以采...
    heming閱讀 3,562評(píng)論 1 8
  • 1.表中的任何列都可以作為主鍵, 只要它滿足以下條件:任意兩行都不具有相同的主鍵值;每一行都必須具有一個(gè)主鍵值( ...
    Cherryjs閱讀 838評(píng)論 0 0
  • 關(guān)系型數(shù)據(jù)庫(kù)和SQL SQL語(yǔ)言的三個(gè)部分DML:Data Manipulation Language,數(shù)據(jù)操縱語(yǔ)...
    Awey閱讀 2,049評(píng)論 0 13
  • 今天參加了一場(chǎng)精英女性盛典,嘉賓們分享著自己的故事,堅(jiān)持著自己的初心,每一位都是如鉆石般閃亮,但來(lái)自日本的近藤麻理...
    王霞般若閱讀 369評(píng)論 0 3
  • 說(shuō)實(shí)話我有一種病,一種叫做假期旅行綜合癥的病狀,因?yàn)槊慨?dāng)接近各種假期,被問(wèn)的最多的就是你要去哪里玩?我承認(rèn)自己的確...
    阿同學(xué)閱讀 264評(píng)論 0 0

友情鏈接更多精彩內(nèi)容