SQL 連接(JOIN)專題

SQL 最強大的功能之一就是能在數(shù)據(jù)檢索查詢的執(zhí)行中聯(lián)結(jié)(join)表疾渣。聯(lián)結(jié)是利用 SQL 的 SELECT 能執(zhí)行的最重要的操作屑埋,很好地理解聯(lián)結(jié)及其語法是學習SQL的一個極為重要的組成部分。另外聚集函數(shù)也可以在聯(lián)結(jié)中進行使用。

SQL 連接(JOIN) 用于把來自兩個或多個表的行結(jié)合起來狞贱。

下圖展示了 LEFT JOIN澈缺、RIGHT JOIN、INNER JOIN塌鸯、OUTER JOIN 相關的 7 種用法侍瑟。

在我們繼續(xù)講解實例之前,我們先列出您可以使用的不同的 SQL JOIN 類型:

INNER JOIN:如果表中有至少一個匹配丙猬,則返回行
LEFT JOIN:即使右表中沒有匹配涨颜,也從左表返回所有的行
RIGHT JOIN:即使左表中沒有匹配,也從右表返回所有的行
FULL JOIN:只要其中一個表中存在匹配茧球,則返回行

前提準備

導入語句

-- --------------------------------------------------------
-- 主機:                           10.1.212.135
-- 服務器版本:                        10.6.5-MariaDB - mariadb.org binary distribution
-- 服務器操作系統(tǒng):                      Win64
-- HeidiSQL 版本:                  11.3.0.6295
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

-- 導出  表 test.access_log 結(jié)構
CREATE TABLE IF NOT EXISTS `access_log` (
  `aid` int(11) NOT NULL AUTO_INCREMENT,
  `site_id` int(11) NOT NULL DEFAULT 0 COMMENT '網(wǎng)站id',
  `count` int(11) NOT NULL DEFAULT 0 COMMENT '訪問次數(shù)',
  `date` date NOT NULL,
  PRIMARY KEY (`aid`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3;

-- 正在導出表  test.access_log 的數(shù)據(jù):~9 rows (大約)
DELETE FROM `access_log`;
/*!40000 ALTER TABLE `access_log` DISABLE KEYS */;
INSERT INTO `access_log` (`aid`, `site_id`, `count`, `date`) VALUES
    (1, 1, 45, '2016-05-10'),
    (2, 3, 100, '2016-05-13'),
    (3, 1, 230, '2016-05-14'),
    (4, 2, 10, '2016-05-14'),
    (5, 5, 205, '2016-05-14'),
    (6, 4, 13, '2016-05-15'),
    (7, 3, 220, '2016-05-15'),
    (8, 5, 545, '2016-05-16'),
    (9, 3, 201, '2016-05-17');
/*!40000 ALTER TABLE `access_log` ENABLE KEYS */;

-- 導出  表 test.websites 結(jié)構
CREATE TABLE IF NOT EXISTS `websites` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '站點名稱',
  `url` varchar(255) NOT NULL DEFAULT '',
  `alexa` int(11) NOT NULL DEFAULT 0 COMMENT 'Alexa 排名',
  `country` char(10) NOT NULL DEFAULT '' COMMENT '國家',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb3;

-- 正在導出表  test.websites 的數(shù)據(jù):~7 rows (大約)
DELETE FROM `websites`;
/*!40000 ALTER TABLE `websites` DISABLE KEYS */;
INSERT INTO `websites` (`id`, `name`, `url`, `alexa`, `country`) VALUES
    (1, 'Google', 'https://www.google.cm/', 1, 'USA'),
    (2, '淘寶', 'https://www.taobao.com/', 13, 'CN'),
    (3, '菜鳥學習網(wǎng)', 'http://www.runoob.com/', 5000, 'CN'),
    (4, '微博', 'http://weibo.com/', 20, 'CN'),
    (5, 'Facebook', 'https://www.facebook.com/', 3, 'USA'),
    (6, '百度', 'https://www.baidu.com/', 4, 'CN'),
    (7, 'stackoverflow', 'http://stackoverflow.com/', 0, 'IND');
/*!40000 ALTER TABLE `websites` ENABLE KEYS */;

/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

預覽數(shù)據(jù)

MariaDB [test]> SELECT * FROM websites;
+----+---------------+---------------------------+-------+---------+
| id | name          | url                       | alexa | country |
+----+---------------+---------------------------+-------+---------+
|  1 | Google        | https://www.google.cm/    |     1 | USA     |
|  2 | 淘寶          | https://www.taobao.com/   |    13 | CN      |
|  3 | 菜鳥學習網(wǎng)    | http://www.runoob.com/    |  5000 | CN      |
|  4 | 微博          | http://weibo.com/         |    20 | CN      |
|  5 | Facebook      | https://www.facebook.com/ |     3 | USA     |
|  6 | 百度          | https://www.baidu.com/    |     4 | CN      |
|  7 | stackoverflow | http://stackoverflow.com/ |     0 | IND     |
+----+---------------+---------------------------+-------+---------+
7 rows in set (0.003 sec)


MariaDB [test]> SELECT * FROM access_log;
+-----+---------+-------+------------+
| aid | site_id | count | date       |
+-----+---------+-------+------------+
|   1 |       1 |    45 | 2016-05-10 |
|   2 |       3 |   100 | 2016-05-13 |
|   3 |       1 |   230 | 2016-05-14 |
|   4 |       2 |    10 | 2016-05-14 |
|   5 |       5 |   205 | 2016-05-14 |
|   6 |       4 |    13 | 2016-05-15 |
|   7 |       3 |   220 | 2016-05-15 |
|   8 |       5 |   545 | 2016-05-16 |
|   9 |       3 |   201 | 2016-05-17 |
+-----+---------+-------+------------+
9 rows in set (0.002 sec)

叉聯(lián)結(jié)

笛卡兒積(cartesian product) 由沒有聯(lián)結(jié)條件的表關系返回的結(jié)果為笛卡兒積庭瑰。檢索出的行的數(shù)目將是第一個表中的行數(shù)乘以第二個表中的行數(shù)。

有時我們會聽到返回稱為叉聯(lián)結(jié)(cross join)的笛卡兒積的聯(lián)結(jié)類型抢埋。

SQL INNER JOIN 內(nèi)聯(lián)結(jié)

INNER JOIN 關鍵字在表中存在匹配時返回行弹灭。

語法

SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;

可省略 INNER 關鍵字:

SELECT column_name(s)
FROM table1
JOIN table2
ON table1.column_name=table2.column_name;

注釋:INNER JOIN 與 JOIN 是相同的督暂。

示例:返回所有網(wǎng)站的訪問記錄

MariaDB [test]> SELECT Websites.name, access_log.count, access_log.date
    -> FROM Websites
    -> INNER JOIN access_log
    -> ON Websites.id=access_log.site_id;
+------------+-------+------------+
| name       | count | date       |
+------------+-------+------------+
| Google     |    45 | 2016-05-10 |
| 菜鳥學習網(wǎng) |   100 | 2016-05-13 |
| Google     |   230 | 2016-05-14 |
| 淘寶       |    10 | 2016-05-14 |
| Facebook   |   205 | 2016-05-14 |
| 微博       |    13 | 2016-05-15 |
| 菜鳥學習網(wǎng) |   220 | 2016-05-15 |
| Facebook   |   545 | 2016-05-16 |
| 菜鳥學習網(wǎng) |   201 | 2016-05-17 |
+------------+-------+------------+
9 rows in set (0.002 sec)

外聯(lián)結(jié)

SQL LEFT JOIN 語法

LEFT JOIN 關鍵字從左表(table1)返回所有的行,即使右表(table2)中沒有匹配穷吮。如果右表中沒有匹配逻翁,則結(jié)果為 NULL。

SQL LEFT JOIN 語法

SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name=table2.column_name;

或:

SELECT column_name(s)
FROM table1
LEFT OUTER JOIN table2
ON table1.column_name=table2.column_name;

下面的 SQL 語句將返回所有網(wǎng)站及他們的訪問量(如果有的話)捡鱼。

MariaDB [test]> SELECT Websites.name, access_log.count, access_log.date
    -> FROM Websites
    -> LEFT JOIN access_log
    -> ON Websites.id=access_log.site_id;
+---------------+-------+------------+
| name          | count | date       |
+---------------+-------+------------+
| Google        |    45 | 2016-05-10 |
| 菜鳥學習網(wǎng)    |   100 | 2016-05-13 |
| Google        |   230 | 2016-05-14 |
| 淘寶          |    10 | 2016-05-14 |
| Facebook      |   205 | 2016-05-14 |
| 微博          |    13 | 2016-05-15 |
| 菜鳥學習網(wǎng)    |   220 | 2016-05-15 |
| Facebook      |   545 | 2016-05-16 |
| 菜鳥學習網(wǎng)    |   201 | 2016-05-17 |
| 百度          |  NULL | NULL       |
| stackoverflow |  NULL | NULL       |
+---------------+-------+------------+
11 rows in set (0.002 sec)

注釋:LEFT JOIN 關鍵字從左表(Websites)返回所有的行八回,即使右表(access_log)中沒有匹配。

SQL RIGHT JOIN 語法

RIGHT JOIN 關鍵字從右表(table2)返回所有的行驾诈,即使左表(table1)中沒有匹配缠诅。如果左表中沒有匹配,則結(jié)果為 NULL乍迄。

SELECT column_name(s)
FROM table1
RIGHT JOIN table2
ON table1.column_name=table2.column_name;

或:

SELECT column_name(s)
FROM table1
RIGHT OUTER JOIN table2
ON table1.column_name=table2.column_name;

注釋:在某些數(shù)據(jù)庫中管引,RIGHT JOIN 稱為 RIGHT OUTER JOIN。

MariaDB [test]> SELECT Websites.name, access_log.count, access_log.date
    -> FROM  access_log
    -> RIGHT JOIN  websites
    -> ON Websites.id=access_log.site_id;
+---------------+-------+------------+
| name          | count | date       |
+---------------+-------+------------+
| Google        |    45 | 2016-05-10 |
| 菜鳥學習網(wǎng)    |   100 | 2016-05-13 |
| Google        |   230 | 2016-05-14 |
| 淘寶          |    10 | 2016-05-14 |
| Facebook      |   205 | 2016-05-14 |
| 微博          |    13 | 2016-05-15 |
| 菜鳥學習網(wǎng)    |   220 | 2016-05-15 |
| Facebook      |   545 | 2016-05-16 |
| 菜鳥學習網(wǎng)    |   201 | 2016-05-17 |
| 百度          |  NULL | NULL       |
| stackoverflow |  NULL | NULL       |
+---------------+-------+------------+
11 rows in set (0.002 sec)

由此我們還能得出結(jié)論就乓,表 A 左外連接表 B 等價于 表 B 右外連接表 A汉匙。

事實上左外聯(lián)結(jié)和右外聯(lián)結(jié)。它們之間的唯一差別是所關聯(lián)的表的順序生蚁。換句話說噩翠,調(diào)整FROM或WHERE子句中表的順序,左外聯(lián)結(jié)可以轉(zhuǎn)換為右外聯(lián)結(jié)邦投。因此伤锚,這兩種外聯(lián)結(jié)可以互換使用,哪個方便就用哪個志衣。

SQL FULL OUTER JOIN 全外連接

FULL OUTER JOIN 關鍵字只要左表(table1)和右表(table2)其中一個表中存在匹配屯援,則返回行.

FULL OUTER JOIN 關鍵字結(jié)合了 LEFT JOIN 和 RIGHT JOIN 的結(jié)果。

注意:MariaDB念脯、MySQL和SQLite 不支持 FULL OUTER JOIN 語法狞洋。你可以在 SQL Server 測試以下實例。

SQL FULL OUTER JOIN 語法

SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;

多個連接的寫法總結(jié)

INNER JOIN 連接兩個數(shù)據(jù)表的用法:

SELECT * FROM 表1 
INNER JOIN 表2 ON 表1.字段號=表2.字段號

INNER JOIN 連接三個數(shù)據(jù)表的用法:

SELECT * FROM 表1
INNER JOIN 表2 ON 表1.字段號=表2.字段號
INNER JOIN 表3 ON 表1.字段號=表3.字段號

自聯(lián)結(jié)

自聯(lián)結(jié)通常作為外部語句绿店,用來替代從相同表中檢索數(shù)據(jù)的使用子查詢語句吉懊。雖然最終的結(jié)果是相同的,但許多 DBMS 處理聯(lián)結(jié)遠比處理子查詢快得多假勿。應該試一下兩種方法借嗽,以確定哪一種的性能更好。

自然聯(lián)結(jié)

標準的聯(lián)結(jié)(前一課中介紹的內(nèi)聯(lián)結(jié))返回所有數(shù)據(jù)转培,相同的列甚至多次出現(xiàn)恶导。自然聯(lián)結(jié)排除多次出現(xiàn),使每一列只返回一次浸须。

自然聯(lián)結(jié)要求你只能選擇那些唯一的列惨寿,一般通過對一個表使用通配符(SELECT*)邦泄,而對其他表的列使用明確的子集來完成。

事實上缤沦,我們迄今為止建立的每個內(nèi)聯(lián)結(jié)都是自然聯(lián)結(jié)虎韵,很可能永遠都不會用到不是自然聯(lián)結(jié)的內(nèi)聯(lián)結(jié)。

聯(lián)結(jié)的性能考慮

注意:性能考慮 DBMS 在運行時關聯(lián)指定的每個表缸废,以處理聯(lián)結(jié)。這種處理可能非常耗費資源驶社,因此應該注意:

  • 不要聯(lián)結(jié)不必要的表企量。聯(lián)結(jié)的表越多,性能下降越厲害亡电。
  • 應該總是提供聯(lián)結(jié)條件届巩,否則會得出笛卡兒積。
SELECT cust_name, cust_contact
FROM customers AS c, orders, orderitems AS oi
where c.cust_id = orders.cust_id and orders.order_num = oi.order_num and oi.prod_id = 'RGAN01';

子查詢并不總是執(zhí)行復雜 SELECT 操作的最有效方法份乒,以下語句也可使用聯(lián)結(jié)的相同查詢恕汇。

多做實驗 正如所見,為執(zhí)行任一給定的SQL操作或辖,一般存在不止一種方法瘾英。很少有絕對正確或絕對錯誤的方法。性能可能會受操作類型颂暇、表中數(shù)據(jù)量缺谴、是否存在索引或鍵以及其他一些條件的影響。因此耳鸯,有必要對不同的選擇機制進行實驗湿蛔,以找出最適合具體情況的方法。

SELECT cust_name, cust_contact FROM customers
where cust_id in (
   select cust_id from orders
   where order_num in (
          select order_num from orderitems where prod_id = 'RGAN01')
);

例題

提問:查找值等于或大于1000的所有訂單號和訂單數(shù)量至少達到這個數(shù)的顧客名稱县爬。

解答:可以使用使用簡單的等聯(lián)結(jié)或ANSI的INNER JOIN語法阳啥。

-- Equijoin syntax
SELECT cust_name, SUM(item_price*quantity) AS total_price
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
 AND Orders.order_num = OrderItems.order_num
GROUP BY cust_name HAVING SUM(item_price*quantity) >= 1000
ORDER BY cust_name;

-- ANSI INNER JOIN syntax
SELECT cust_name, SUM(item_price*quantity) AS total_price
FROM Customers 
 INNER JOIN Orders ON Customers.cust_id = Orders.cust_id 
 INNER JOIN OrderItems ON Orders.order_num = OrderItems.order_num
GROUP BY cust_name
HAVING SUM(item_price*quantity) >= 1000
ORDER BY cust_name;

參考

SQL UNION 操作符 | 菜鳥教程
https://www.runoob.com/sql/sql-union.html

SQL必知必會(第5版)-本·福達-微信讀書https://weread.qq.com/web/reader/f7632a30720befadf7636bb

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市财喳,隨后出現(xiàn)的幾起案子察迟,更是在濱河造成了極大的恐慌,老刑警劉巖纲缓,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卷拘,死亡現(xiàn)場離奇詭異,居然都是意外死亡祝高,警方通過查閱死者的電腦和手機栗弟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來工闺,“玉大人乍赫,你說我怎么就攤上這事瓣蛀。” “怎么了雷厂?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵惋增,是天一觀的道長。 經(jīng)常有香客問我改鲫,道長诈皿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任像棘,我火速辦了婚禮稽亏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缕题。我一直安慰自己截歉,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布烟零。 她就那樣靜靜地躺著瘪松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锨阿。 梳的紋絲不亂的頭發(fā)上宵睦,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音群井,去河邊找鬼状飞。 笑死,一個胖子當著我的面吹牛书斜,可吹牛的內(nèi)容都是我干的诬辈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼荐吉,長吁一口氣:“原來是場噩夢啊……” “哼焙糟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起样屠,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤穿撮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痪欲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悦穿,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年业踢,在試婚紗的時候發(fā)現(xiàn)自己被綠了栗柒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡知举,死狀恐怖瞬沦,靈堂內(nèi)的尸體忽然破棺而出太伊,到底是詐尸還是另有隱情,我是刑警寧澤逛钻,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布僚焦,位于F島的核電站,受9級特大地震影響曙痘,放射性物質(zhì)發(fā)生泄漏芳悲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一边坤、第九天 我趴在偏房一處隱蔽的房頂上張望芭概。 院中可真熱鬧,春花似錦惩嘉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至殿较,卻和暖如春耸峭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淋纲。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工劳闹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洽瞬。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓本涕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親伙窃。 傳聞我的和親對象是個殘疾皇子菩颖,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容