JOIN是SQL當中扮演非常重要的角色,它的功能就是拿來將不同資料表進行關聯。
Day 15 我們知道為什麼要正規化、Day 16 了解關於Key的作用,所以今天使用JOIN之前,先來建立已經正規化且設置PERMARY KEY的資料表,不過FOREIGN KEY會稍後再加入,先宣告成INT NOT NULL就好。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(300) NOT NULL,
email VARCHAR(300) NOT NULL,
address_id INT NOT NULL -- 稍後補上FOREIGN KEY正式語法
);
CREATE TABLE addresses (
id SERIAL PRIMARY KEY,
address VARCHAR(300) NOT NULL,
district_id INT NOT NULL -- 稍後補上FOREIGN KEY正式語法
);
CREATE TABLE districts (
id SERIAL PRIMARY KEY,
name VARCHAR(300) UNIQUE NOT NULL
);
接著我們來插入一些資料,因為users資料表的address_id欄位需要addresses資料表的id,而addresses資料表的district_id欄位需要districts資料表的id欄位,寫入資料必需先寫districts,再寫addresses再寫users。
INSERT INTO districts (name)
VALUES ('雲林縣'),('嘉義縣'),('台南市'),('高雄市');
INSERT INTO addresses (address, district_id)
VALUES
('雲林縣斗南鎮賢者模式路0號', 1),
('嘉義縣民雄鄉肉包路35號',2),
('台南市歸仁區沙城路980號',3),
('高雄市旗津區砲台路168號',4);
INSERT INTO users (name, email, address_id)
VALUES
('鄭小元', 'smallbaby@gmail.com', 1),
('李小刀', 'okok148@gmail.com',2),
('詹小舜', 'lbjking2003@gmail.com',3);
SELECT *
FROM users
INNER JOIN addresses ON users.address_id = addresses.id;
我們不一定要在ON明確標示出資料表(table)名稱,只要這串SQL所執行的欄位可以清楚識別是屬於哪一個資料表的時候,你是可以省略的,例如address_id只有users資料表有,addresses則沒有,那麼就可以只寫address_id就好,資料庫自己會知道是個資料表,不過再看到id則是兩個資料表都有,你是不能省略的,一定要標示出你是要用哪一個id,所以此例要寫出addresses.id。
SELECT *
FROM users
INNER JOIN addresses ON address_id = addresses.id;
通常使用AS來替換資料表名稱的目的是為了縮短資料表名稱,例如users替換成u,addresses替換成a,這不是必要的作法,但是如果你開始認為SQL指令長到看起來有點吃力的時候,那麼就可以試試使用AS來命名。
SELECT a.id, name, email, address
FROM users AS u
INNER JOIN addresses AS a ON u.address_id = a.id;
另外替換名稱其實也可以省略AS,甚至有些資料庫只接受這種寫法,不過我自己是喜歡加上AS,對我而言感覺更為明確。
SELECT u.id, name, email, address
FROM users u
INNER JOIN addresses a ON u.address_id = a.id;
JOIN在很多專案中不會只有JOIN一次而已,會依據當初拆分的表格數或要取出來的資料範圍來看要JOIN幾張資料表,在這個例子只有三張資料表,我們可以JOIN之後再JOIN,用法是一樣的,如下例所示,要稍微注意一下SELECT的內容在name有衝突,因此要標記是哪個資料表的name,然後d.name在輸出欄位的時候感覺不夠明確,因此使用AS命名為district,如此一來就能依據這個指令組出資料的全貌。
SELECT u.id, u.name, email, d.name AS district, address
FROM users AS u
INNER JOIN addresses AS a ON u.address_id = a.id
INNER JOIN districts AS d ON a.district_id = d.id;
SELECT u.id, name, email, address
FROM users AS u
LEFT JOIN addresses AS a ON u.address_id = a.id;
你會發現跟INNER JOIN找出來的資料是一樣的,不過如果我們JOIN的角度改變,我們改用從addresses表格去找,而且還要跟users連接,JOIN關係的左邊變成了addresses,也就是addresses表格中有的資料就要顯示出來,例如高雄市旗津區砲台路168號找不到對應的人,顯示出來的user就會是NULL。
SELECT u.id, name, email, address
FROM addresses AS a
LEFT JOIN users AS u ON a.id = u.address_id;
RIGHT JOIN 的觀念同LEFT JOIN,只是要全抓的資料從左邊那張變成右邊那張,不過不見得需要用到RIGHT JOIN,因為可以透過JOIN資料表位置對調就能用LEFT JOIN找出一樣的內容。
SELECT u.id, name, email, address
FROM users AS u
RIGHT JOIN addresses AS a ON u.address_id = a.id;
笛卡爾乘積運算,通俗一點的說法是把表格所有的配對可能都給列出來,例如幫男女進行配對。
CREATE TABLE boys (
name VARCHAR(300)
);
CREATE TABLE grils (
name VARCHAR(300)
);
INSERT INTO boys (name)
VALUES ('王小明'),('蘇大聰'),('陳小龍');
INSERT INTO grils (name)
VALUES ('林小美'),('吳大妞'),('張小柯');
算出每個組合的速配率,是不是很好玩XD
SELECT b.name AS 男方, g.name AS 女方, FLOOR(RANDOM()*100+1) AS 速配率
FROM boys AS b
CROSS JOIN grils AS g;
UNION是另一種可以把資料表資料合併起來的方式,不過觀念跟JOIN差很多,事實上UNION比JOIN簡單很多,它必需是資料表的欄位數量跟順序都一樣,可以看成是相同資料的合併,例如我們可以靠這個方列出所有參加配對遊戲的人,不分男女老少,UNION的使用情境會比JOIN少很多,因為要找到相同型態跟欄位又分開放不太容易,通常是偏向特定資料管理面的因素居多。
SELECT * FROM boys
UNION
SELECT * FROM grils;