SQL基礎-第7章 集合運算
阿新 • • 發佈:2021-11-19
7-1 表的加減法
什麼是集合運算
集合運算,就是對滿足同一規則的記錄進行的加減等四則運算
表的加法——UNION
集合運算子會除去重複的記錄。
-- 建立表Product2(商品2) CREATE TABLE Product2 (product_id CHAR(4) NOT NULL, product_name VARCHAR(100) NOT NULL, product_type VARCHAR(32) NOT NULL, sale_price INTEGER , purchase_price INTEGER , regist_date DATE , PRIMARY KEY (product_id)) DEFAULT CHARSET=utf8; -- 將資料插入到表Product2(商品2)中 START TRANSACTION; INSERT INTO Product2 VALUES ('0001', 'T恤衫' ,'衣服', 1000, 500, '2008-09-20'); INSERT INTO Product2 VALUES ('0002', '打孔器', '辦公用品', 500, 320, '2009-09-11'); INSERT INTO Product2 VALUES ('0003', '運動T恤', '衣服', 4000, 2800, NULL); INSERT INTO Product2 VALUES ('0009', '手套', '衣服', 800, 500, NULL); INSERT INTO Product2 VALUES ('0010', '水壺', '廚房用具', 2000, 1700, '2009-09-20'); COMMIT; -- 使用UNION對錶進行加法運算 SELECT product_id, product_name FROM Product UNION SELECT product_id, product_name FROM Product2;
集合運算的注意事項
- 作為運算物件的記錄的列數必須相同
-- 列數不一致時會發生錯誤
SELECT product_id, product_name
FROM Product
UNION
SELECT product_id, product_name, sale_price
FROM Product2;
- 作為運算物件的記錄中列的型別必須一致
-- 資料型別不一致時會發生錯誤
SELECT product_id, sale_price
FROM Product
UNION
SELECT product_id, regist_date
FROM Product2;
- 可以使用任何SELECT語句,但ORDER BY子句只能在最後使用一次
-- ORDER BY子句只在最後使用一次
SELECT product_id, product_name
FROM Product
WHERE product_type = '廚房用具'
UNION
SELECT product_id, product_name
FROM Product2
WHERE product_type = '廚房用具'
ORDER BY product_id;
包含重複行的集合運算——ALL選項
在集合運算子中使用ALL選項,可以保留重複行。
-- 保留重複行 SELECT product_id, product_name FROM Product UNION ALL SELECT product_id, product_name FROM Product2;
7-2 聯結(以列為單位對錶進行聯結)
什麼是聯結
就是將其他表中的列新增過來,進行“新增列”的運算
內聯結——INNER-JOIN
- 內聯結要點—FROM子句
-- 進行聯結時需要在FROM子句中使用多張表。
FROM ShopProduct AS SP INNER JOIN Product AS P
- 內聯結要點--ON子句
-- 進行內聯結時必須使用ON子句,並且要書寫在FROM和WHERE之間。
ON SP.product_id = P.product_id;
- 內聯結要點-—SELECT子句
-- 使用聯結時SELECT子句中的列需要按照“<表的別名>.<列名>”的格式進行書寫。
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
- 將兩張表進行內聯結
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
FROM ShopProduct AS SP INNER JOIN Product AS P
ON SP.product_id = P.product_id;
-- 內聯結和WHERE子句結合使用
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
FROM ShopProduct AS SP INNER JOIN Product AS P
ON SP.product_id = P.product_id
WHERE SP.shop_id = '000A';
外聯結——OUTER-JOIN
- 外聯結要點-選取出單張表中全部的資訊
- 外聯結要點-每張表都是主表嗎?
- 使用 LEFT 時 FROM 子句中寫在左側的表是主表
- 使用 RIGHT 時右側的表是主表
-- 將兩張表進行外聯結
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
FROM ShopProduct AS SP RIGHT OUTER JOIN Product AS P
ON SP.product_id = P.product_id;
-- 改寫後外聯結的結果完全相同
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
FROM Product AS P LEFT OUTER JOIN ShopProduct AS SP
ON SP.product_id = P.product_id;
3張以上的表的聯結
-- 建立InventoryProduct表並向其中插入資料
-- DDL :建立表
CREATE TABLE InventoryProduct
( inventory_id CHAR(4) NOT NULL,
product_id CHAR(4) NOT NULL,
inventory_quantity INTEGER NOT NULL,
PRIMARY KEY (inventory_id, product_id));
-- DML :插入資料
START TRANSACTION;
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0001', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0002', 120);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0003', 200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0004', 3);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0005', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0006', 99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0007', 999);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P001', '0008', 200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0001', 10);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0002', 25);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0003', 34);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0004', 19);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0005', 99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0006', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0007', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity)
VALUES ('P002', '0008', 18);
COMMIT;
-- 對3張表進行內聯結
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price, IP.inventory_quantity
FROM ShopProduct AS SP INNER JOIN Product AS P ON SP.product_id = P.product_id
INNER JOIN InventoryProduct AS IP ON SP.product_id = IP.product_id
WHERE IP.inventory_id = 'P001';
交叉聯結——CROSS-JOIN 笛卡兒積
-- 將兩張表進行交叉聯結
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name
FROM ShopProduct AS SP CROSS JOIN Product AS P;
聯結的特定語法和過時語法
-- 使用過時語法的內聯結
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
FROM ShopProduct SP, Product P
WHERE SP.product_id = P.product_id
AND SP.shop_id = '000A';
-- DDL :建立表
CREATE TABLE Skills
(skill VARCHAR(32),
PRIMARY KEY(skill));
CREATE TABLE EmpSkills
(emp VARCHAR(32),
skill VARCHAR(32),
PRIMARY KEY(emp, skill))
DEFAULT CHARSET=utf8;
-- DML :插入資料
START TRANSACTION;
INSERT INTO Skills VALUES('Oracle');
INSERT INTO Skills VALUES('UNIX');
INSERT INTO Skills VALUES('Java');
INSERT INTO EmpSkills VALUES('相田', 'Oracle');
INSERT INTO EmpSkills VALUES('相田', 'UNIX');
INSERT INTO EmpSkills VALUES('相田', 'Java');
INSERT INTO EmpSkills VALUES('相田', 'C#');
INSERT INTO EmpSkills VALUES('神崎', 'Oracle');
INSERT INTO EmpSkills VALUES('神崎', 'UNIX');
INSERT INTO EmpSkills VALUES('神崎', 'Java');
INSERT INTO EmpSkills VALUES('平井', 'UNIX');
INSERT INTO EmpSkills VALUES('平井', 'Oracle');
INSERT INTO EmpSkills VALUES('平井', 'PHP');
INSERT INTO EmpSkills VALUES('平井', 'Perl');
INSERT INTO EmpSkills VALUES('平井', 'C++');
INSERT INTO EmpSkills VALUES('若田部', 'Perl');
INSERT INTO EmpSkills VALUES('渡來', 'Oracle');
COMMIT;
-- 選取出掌握所有3個領域的技術的員工
SELECT DISTINCT emp
FROM EmpSkills ES1
WHERE NOT EXISTS
(SELECT skill
FROM Skills
EXCEPT
SELECT skill
FROM EmpSkills ES2
WHERE EP1.emp = ES2.emp);
練習題
7.1 請說出下述 SELECT 語句的結果。
-- 使用本章中的Product表
SELECT *
FROM Product
UNION
SELECT *
FROM Product
INTERSECT
SELECT *
FROM Product
ORDER BY product_id;
會將 Product 表中的 8 行記錄原封不動地選取出來。
7.2 7-2 節的程式碼清單 7-11 中列舉的外聯結的結果中,高壓鍋和圓珠筆 2 條 記錄的商店編號( shop_id)和商店名稱( shop_name)都是 NULL。
請使用字串“不確定”替換其中的 NULL。期望結果如下所示。
執行結果
shop_id | shop_name | product_id | product_name | sale_price |
---|---|---|---|---|
000A | 東京 | 0002 | 打孔器 | 500 |
000A | 東京 | 0003 | 運動T恤 | 4000 |
000A | 東京 | 0001 | T恤衫 | 1000 |
000B | 名古屋 | 0006 | 叉子 | 500 |
000B | 名古屋 | 0002 | 打孔器 | 500 |
000B | 名古屋 | 0003 | 運動T恤 | 4000 |
000B | 名古屋 | 0004 | 菜刀 | 3000 |
000B | 名古屋 | 0007 | 擦菜板 | 880 |
000C | 大阪 | 0006 | 叉子 | 500 |
000C | 大阪 | 0007 | 擦菜板 | 880 |
000C | 大阪 | 0003 | 運動T恤 | 4000 |
000C | 大阪 | 0004 | 菜刀 | 3000 |
000D | 福岡 | 0001 | T恤衫 | 1000 |
不確定 | 不確定 | 0005 | 高壓鍋 | 6800 |
不確定 | 不確定 | 0008 | 圓珠筆 | 100 |
將商店編號和商店名稱輸出為"不確定"
SELECT COALESCE(s.shop_id, '不確定') as shop_id,
COALESCE(s.shop_name, '不確定') as shop_name,
p.product_id, p.product_name, p.sale_price
FROM ShopProduct s RIGHT OUTER JOIN Product p
ON s.product_id = p.product_id;