1. 程式人生 > 其它 >SQL基礎-第7章 集合運算

SQL基礎-第7章 集合運算

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;