大家好,我是隻談技術不剪髮的 Tony 老師。今天給大家解析一下 LeetCode 數據庫題庫中的第 175 題:組合兩個表。
首先,我們會對題目進行解析並給出 MySQL、Oracle 以及 SQL Server 三種數據庫的實現方法;然後,我們還會針對相關的 SQL 知識進行擴展介紹。
題目來源:力扣(LeetCode)。
題目描述
表1:Person
+-------------+---------+
| 列名 | 類型 |
+-------------+---------+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
+-------------+---------+
PersonId 是上表主鍵
表2:Address
+-------------+---------+
| 列名 | 類型 |
+-------------+---------+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
+-------------+---------+
AddressId 是上表主鍵
編寫一個 SQL 查詢,滿足條件:無論 person 是否有地址信息,都需要基於上述兩表提供 person 的以下信息:
FirstName, LastName, City, State
建表腳本和示例數據:
Create table Person (PersonId int, FirstName varchar(255), LastName varchar(255));
Create table Address (AddressId int, PersonId int, City varchar(255), State varchar(255));
Truncate table Person;
insert into Person (PersonId, LastName, FirstName) values ('1', 'Wang', 'Allen');
Truncate table Address;
insert into Address (AddressId, PersonId, City, State) values ('1', '2', 'New York City', 'New York');
題目解析
查詢結果要求返回的字段中 FirstName 和 LastName 來自 Person 表,City 和 State 來自 Address 表;顯然我們需要使用連接(Join)查詢關聯這兩個表。
從表結構可以看出, Address 表中的 PersonId 字段對應的是 Person 表上的 PersonId;同時考慮到並不是每個人都有地址信息,所以我們應該使用外連接確保會返回所有的人員,而不是使用內連接。
MySQL 實現
MySQL 可以使用 left join 或者 right join 實現題目要求:
select p.FirstName, p.LastName, a.City, a.State
from Person p
left join Address a
on p.PersonId = a.PersonId;
FirstName|LastName|City|State|
---------|--------|----|-----|
Allen |Wang | | |
從查詢結果可以看出,雖然 Allen.Wang 沒有地址信息,仍然返回了姓名。當然,我們也可以使用 right join,只需要將兩個表的順序交換一下就可以了。
Oracle 實現
首先,Oracle 可以使用像 MySQL 一樣的 SQL 語句實現題目要求。
其次,Oracle 還支持專有的外連接語法(ANSI SQL/86 標準):
select p.FirstName, p.LastName, a.City, a.State
from Person p, Address a
where p.PersonId = a.PersonId(+);
FIRSTNAME|LASTNAME|CITY|STATE|
---------|--------|----|-----|
Allen |Wang | | |
WHERE 條件中等號右側的 (+) 表示 Address 表中的數據爲空時仍然返回 Person 中的記錄,也就是左外連接。
⚠️這種 ANSI SQL/86 標準的外連接語法不具備移植性,不推薦使用。
SQL Server 實現
SQL Server 可以使用像 MySQL 一樣的 SQL 語句實現題目要求。
知識擴展
SQL 連接查詢可以從兩個或多個表中獲取關聯數據,SQL 中的連接查詢主要包括以下類型:
- INNER JOIN,內連接;
- LEFT OUTER JOIN,左外連接;
- RIGHT OUTER JOIN,右外連接;
- FULL OUTER JOIN,全外連接;
- CROSS JOIN,交叉連接;
- NATURAL JOIN,自然連接;
- Self Join,自連接;
- Anti Join,反連接;
- Semi Join,半連接。
其中,左外連接、右外連接以及全外連接統稱爲外連接(OUTER JOIN)。
連接查詢中的 ON 子句與 WHERE 子句類似,可以支持各種條件運算符(=、>=、!=、BETWEEN 等)‘但最常用的是等值連接(=),我們主要介紹這種條件的連接查詢。
內連接
內連接使用關鍵字INNER JOIN
表示,也可以簡寫成JOIN
; 內連接只返回兩個表中匹配的數據行。參考以下示意圖(基於兩個表的 id 進行連接):
其中,id = 1 和 id = 3 是兩個表中匹配的數據,因此內連接返回了 2 行記錄。
左外連接
左外連接使用關鍵字LEFT OUTER JOIN
表示,也可以簡寫成LEFT JOIN
; 左外連接返回左表中所有的數據行;對於右表中的數據,如果沒有匹配的值就返回空值。參考以下示意圖(基於兩個表的 id 進行連接):
其中,id = 2 在 table1 中存在,在 table2 中不存在;左外連接仍然會返回該記錄,只是對於 table2 中的列,返回的是空值。
右外連接
右外連接使用關鍵字RIGHT OUTER JOIN
表示,也可以簡寫成RIGHT JOIN
; 右外連接返回右表中所有的數據行;對於左表中的數據,如果沒有匹配的值就返回空值。參考以下示意圖(基於兩個表的 id 進行連接):
右外連接和左外連接可以互相替換,以下語句是等價的:
table1 RIGHT JOIN table2
table2 LEFT JOIN table1
全外連接
全外連接使用關鍵字FULL OUTER JOIN
表示,也可以簡寫成FULL JOIN
。全外連接等效於左外連接加上右外連接,返回左表和右表中所有的數據行;對於左表和右表中的數據,如果沒有匹配的值就返回空值。參考以下示意圖(基於兩個表的 id 進行連接):
需要注意的是,對於重複的行(id = 1 和 id = 3),只返回一次記錄。
⚠️MySQL 目前不支持全外連接。
對於 Oracle 和 SQL Server 而言,Person 表和 Address 表全外連接查詢的結果如下:
select p.FirstName, p.LastName, a.City, a.State
from Person p
full join Address a
on p.PersonId = a.PersonId;
FIRSTNAME|LASTNAME|CITY |STATE |
---------|--------|-------------|--------|
| |New York City|New York|
Allen |Wang | | |
交叉連接
交叉連接也稱爲笛卡爾積(Cartesian product),使用關鍵字CROSS JOIN
表示。兩個表的笛卡爾積相當於一個表的所有行和另一個表的所有行兩兩組合,結果的數量爲兩個表的行數相乘。假如第一個表有 100 行,第二個表有 200 行,它們的交叉連接將會產生 100 × 200 = 20000 行結果。參考以下示意圖(基於兩個表的 id 進行連接):
自然連接
自然連接並不是一種新的連接方式,而是特殊情況下的簡寫語法。如果我們使用兩個表中的所有同名字段進行等值連接,可以使用NATURAL JOIN
簡化查詢語句。例如:
select *
from Person p
natural join Address a;
Person 表和 Address 表擁有 1 個同名字段 PersonId,因此上面的查詢等價於使用 PersonId 進行等值連接的內查詢。
⚠️SQL Server 目前不支持自然連接語法。
自連接
自連接(Self join)是一種特殊的連接,它是指連接操作符的兩邊都是同一個表,即把一個表和它自己進行連接。
自連接本質上並沒有什麼特殊之處,主要用於處理那些對自己進行了引用的表。例如員工表中的經理字段指向了員工表自身的其他員工編號,通過自連接可以獲取上下級的管理關係。
半連接
半連接用於返回左表中與右表至少匹配一次的數據行,通常體現爲 EXISTS 或者 IN 子查詢。半連接的示意圖如下:
半連接只會返回左表中的數據,右表只用於條件判斷。另外,即使右表中存在多個匹配的數據,左邊中的數據只返回一次。半連接通常用於存在性判斷,例如哪些顧客購買了產品,而不需要知道他們購買的具體產品。
以下語句用於查找擁有地址信息的人員:
select *
from Person p
where exists(
select 1
from Address a
where a.PersonId = p.PersonId
);
PersonId|FirstName|LastName|
--------|---------|--------|
反連接
反連接用於返回左表中與右表不匹配的數據行,通常體現爲 NOT EXISTS 或者 NOT IN 子查詢。反連接的示意圖如下:
反連接只會返回左表中的數據,右表只用於條件判斷。反查詢常見的應用包括:查找沒有員工的部門信息,或者沒有購買任何產品的顧客信息等。例如,以下語句返回了沒有地址信息的人員:
select *
from Person p
where not exists(
select 1
from Address a
where a.PersonId = p.PersonId
);
PersonId|FirstName|LastName|
--------|---------|--------|
1|Allen |Wang |
如果覺得文章對你有用,請不要白嫖!歡迎關注❤️、評論📝、點贊👍!