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