![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230825164340432-1938857156.png) # 1. 連接 ## 1.1. 笛卡兒積 ### 1.1.1. 交叉連接(cross join) ### 1.1 ...
1. 連接
1.1. 笛卡兒積
1.1.1. 交叉連接(cross join)
1.1.2. 查詢並沒有指定兩個數據表應該如何連接,資料庫伺服器就生成了笛卡兒積
1.1.2.1. 兩個數據表的所有排列組合
1.1.3. 很少會用到(至少不會特意用到)
1.1.3.1. 使用頻率並不高
1.1.4. mysql
-> SELECT c.first_name, c.last_name, a.address
-> FROM customer c JOIN address a;
1.1.5. 為2020年的每一天生成一行,但是資料庫中沒有包含每天一行的數據表
1.1.5.1. 涉及交叉連接、外連接、日期函數、分組、集合運算(union all)和聚合函數(count())
1.1.5.2. sql
SELECT '2020-01-01' dt
UNION ALL
SELECT '2020-01-02' dt
UNION ALL
SELECT '2020-01-03' dt
UNION ALL
...
...
...
SELECT '2020-12-29' dt
UNION ALL
SELECT '2020-12-30' dt
UNION ALL
SELECT '2020-12-31' dt
1.1.5.3. mysql
-> SELECT ones.num + tens.num + hundreds.num
-> FROM
-> (SELECT 0 num UNION ALL
-> SELECT 1 num UNION ALL
-> SELECT 2 num UNION ALL
-> SELECT 3 num UNION ALL
-> SELECT 4 num UNION ALL
-> SELECT 5 num UNION ALL
-> SELECT 6 num UNION ALL
-> SELECT 7 num UNION ALL
-> SELECT 8 num UNION ALL
-> SELECT 9 num) ones
-> CROSS JOIN
-> (SELECT 0 num UNION ALL
-> SELECT 10 num UNION ALL
-> SELECT 20 num UNION ALL
-> SELECT 30 num UNION ALL
-> SELECT 40 num UNION ALL
-> SELECT 50 num UNION ALL
-> SELECT 60 num UNION ALL
-> SELECT 70 num UNION ALL
-> SELECT 80 num UNION ALL
-> SELECT 90 num) tens
-> CROSS JOIN
-> (SELECT 0 num UNION ALL
-> SELECT 100 num UNION ALL
-> SELECT 200 num UNION ALL
-> SELECT 300 num) hundreds;
1.1.5.4. 如果生成{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}、{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}和{0,100, 200, 300}這3個集合的笛卡兒積,並將這3列的值相加,就可以得到包含0~399的所有數值的400行結果集
1.1.5.5. mysql
-> SELECT DATE_ADD('2020-01-01',
-> INTERVAL (ones.num + tens.num + hundreds.num) DAY) dt
-> FROM
-> (SELECT 0 num UNION ALL
-> SELECT 1 num UNION ALL
-> SELECT 2 num UNION ALL
-> SELECT 3 num UNION ALL
-> SELECT 4 num UNION ALL
-> SELECT 5 num UNION ALL
-> SELECT 6 num UNION ALL
-> SELECT 7 num UNION ALL
-> SELECT 8 num UNION ALL
-> SELECT 9 num) ones
-> CROSS JOIN
-> (SELECT 0 num UNION ALL
-> SELECT 10 num UNION ALL
-> SELECT 20 num UNION ALL
-> SELECT 30 num UNION ALL
-> SELECT 40 num UNION ALL
-> SELECT 50 num UNION ALL
-> SELECT 60 num UNION ALL
-> SELECT 70 num UNION ALL
-> SELECT 80 num UNION ALL
-> SELECT 90 num) tens
-> CROSS JOIN
-> (SELECT 0 num UNION ALL
-> SELECT 100 num UNION ALL
-> SELECT 200 num UNION ALL
-> SELECT 300 num) hundreds
-> WHERE DATE_ADD('2020-01-01',
-> INTERVAL (ones.num + tens.num + hundreds.num) DAY) < '2021-01-01'
-> ORDER BY 1;
1.1.5.6. 無須人為介入,結果集會自動包含額外的閏日(2月29日),這是由資料庫伺服器通過將2020年1月1日加上59天計算得出
1.2. 內連接
1.2.1. 增加了連接類型(註意關鍵字inner)
1.2.2. mysql
-> SELECT c.first_name, c.last_name, a.address
-> FROM customer c JOIN address a
-> ON c.address_id = a.address_id;
1.2.3. sql
SELECT c.first_name, c.last_name, a.address
FROM customer c INNER JOIN address a
ON c.address_id = a.address_id;
1.2.4. 如果用於連接兩個數據表的列名相同,則可以使用using子句替代on
1.2.4.1. sql
SELECT c.first_name, c.last_name, a.address
FROM customer c INNER JOIN address a
USING (address_id);
1.2.4.2. using是一種只能在某些特定情況下使用的簡寫表示法
1.3. 外連接(outer join)
1.3.1. 如果要將一個數據表中的所有行全部納入結果集,不管其在另一個數據表中是否存在匹配
1.3.2. mysql
-> SELECT f.film_id, f.title, count(i.inventory_id) num_copies
-> FROM film f
-> LEFT OUTER JOIN inventory i
-> ON f.film_id = i.film_id
-> GROUP BY f.film_id, f.title;
1.3.3. 關鍵字left和right只是告知伺服器哪個數據表的數據可以不足
1.3.4. outer關鍵字是可選的,可以使用A left join B來代替
1.4. 自然連接(natural join)
1.4.1. 依靠多個數據表之間相同的列名來推斷適合的連接條件
1.4.2. mysql
-> SELECT c.first_name, c.last_name, date(r.rental_date)
-> FROM customer c
-> NATURAL JOIN rental r;
Empty set (0.04 sec)
1.4.3. 資料庫伺服器檢查數據表定義並添加了連接條件r.customer_id = c.customer_id
1.4.4. 應該避免使用這種連接類型,而使用帶有顯式連接條件的內連接
1.5. 連接的舊方法
1.5.1. mysql
-> SELECT c.first_name, c.last_name, a.address
-> FROM customer c, address a
-> WHERE c.address_id = a.address_id;
1.5.2. 不需要on子句
1.5.3. from子句中的數據表名以逗號分隔
1.5.4. 連接條件出現在where子句中
1.6. ANSI連接語法
1.6.1. 連接條件和過濾條件被分隔在兩個不同的子句中(on子句和where子句),使得查詢語句更易於理解
1.6.2. 兩個數據表的連接條件出現在其各自單獨的on子句中,這樣就不太可能錯誤地忽略連接條件
1.6.3. 使用SQL92連接語法的查詢語句可以在各種資料庫伺服器間移植,而舊語法在不同伺服器上的表現略有不同
1.7. 連接順序
1.7.1. 各數據表在from子句中出現的順序並不重要
1.7.1.1. 伺服器使用從資料庫對象收集的統計信息,在3個數據表中選擇一個作為起點(所選擇的數據表被稱為驅動表),然後確定其他數據表的連接順
1.7.2. 如果認為查詢語句中的數據表應該始終以特定的順序連接,可以將數據表按照需要的順序排列
1.7.2.1. 在MySQL中指定straight_join關鍵字
1.7.2.1.1. sql
SELECT STRAIGHT_JOIN c.first_name, c.last_name, ct.city
FROM city ct
INNER JOIN address a
ON a.city_id = ct.city_id
INNER JOIN customer c
ON c.address_id = a.address_id
1.7.2.2. 在SQL Server中請求force order選項
1.7.2.3. 在Oracle Database中使用ordered或leading優化器
1.7.3. 三路外連接
1.7.3.1. mysql
-> SELECT f.film_id, f.title, i.inventory_id, r.rental_date
-> FROM film f
-> LEFT OUTER JOIN inventory i
-> ON f.film_id = i.film_id
-> LEFT OUTER JOIN rental r
-> ON i.inventory_id = r.inventory_id
-> WHERE f.film_id BETWEEN 13 AND 15;
1.8. 自連接
1.8.1. mysql
-> SELECT f.title, f_prnt.title prequel
-> FROM film f
-> INNER JOIN film f_prnt
-> ON f_prnt.film_id = f.prequel_film_id
-> WHERE f.prequel_film_id IS NOT NULL;
2. 集合
2.1. 在對兩個數據集執行集合運算時
2.1.1. 兩個數據集的列數必須相等
2.1.2. 兩個數據集各列的數據類型必須相同
2.1.2.1. 資料庫伺服器必須能夠將一種數據類型轉換成另一種數據類型
2.2. union運算符
2.2.1. mysql
-> SELECT 1 num, 'abc' str
-> UNION
-> SELECT 9 num, 'xyz' str;
2.2.2. union會對組合後的集合進行排序並去除重覆項
2.2.3. union all則不然
2.3. intersect運算符
2.3.1. ANSI SQL規範中定義了用於執行交集運算的intersect運算符
2.3.2. ANSI SQL規範還提供了不去除重覆行的intersect all運算符
2.3.2.1. 唯一實現了intersect all運算符的資料庫伺服器是IBM DB2 Universal Server
2.3.3. MySQL 8.0版還未實現intersect運算符
2.3.4. Oracle或SQL Server 2008中可以使用
2.4. except運算符
2.4.1. ANSI SQL規範提供了執行差集運算的except運算符
2.4.2. ANSI SQL規範還提供了except all運算符
2.4.2.1. 只有IBM的 DB2 Universal Server實現了該運算符
2.4.3. MySQL 8.0版也沒有實現except運算符
2.4.4. Oracle Database需要使用非ANSI相容的minus運算符替代except運算符
2.5. 對符合查詢結果排序
2.5.1. mysql
-> SELECT a.first_name fname, a.last_name lname
-> FROM actor a
-> WHERE a.first_name LIKE 'J%' AND a.last_name LIKE 'D%'
-> UNION ALL
-> SELECT c.first_name, c.last_name
-> FROM customer c
-> WHERE c.first_name LIKE 'J%' AND c.last_name LIKE 'D%'
-> ORDER BY lname, fname;