還記得昨天我們介紹最簡單的SQL語法,針對一個關係表裡面的資料作查詢。今天我們要再增加一點複雜度,試著查詢多個關係表。
但是在開始之前,我們要先匯入一點資料,讓想一起操作的讀者可以一起操作。請將下面這些指令複製貼入Workbench 然後執行,我們過幾天會在會再比較後面的時候再做深入的解釋。
Create Database myShop;
use myShop;
Create table Menu (
MenuID varchar(4) Not Null,
Item varchar(100) Not Null,
Price integer Not Null,
TypeID varchar(4)
);
Create table FoodType(
TypeID varchar(4) Not Null,
TypeName varchar(20) Not Null
);
Create table Customer (
CustomerID varchar(4) Not Null,
Name varchar(100) Not Null,
Gender varchar(1) Default Null,
Level varchar(20) Default Null
);
Create table Review (
ReviewID varchar(4) Not Null,
CustomerID varchar(4) Not Null,
MenuID varchar(4) Not Null,
Rating integer Default Null,
Review varchar(400) Default Null
);
Insert into Menu (MenuID, Item, Price, TypeID) Values
("M001", "Lemon Cake", 80, "T001"),
("M002", "Oolong Tea", 30, "T002"),
("M003", "Cheese Cake", 75, "T001"),
("M004", "Green Tea", 30, "T002"),
("M005", "Milk Tea", 40, "T002"),
("M006", "Magic Cookies", 99, NULL);
Insert into FoodType (TypeID, TypeName) Values
("T001", "Cake"),
("T002", "Drink"),
("T003", "Candy");
Insert into Customer (CustomerID, Name, Gender, Level) Values
("C001", "Alice", 'F', "Gold"),
("C002", "Bob", 'M', "Silver"),
("C003", "Chris", NULL, NULL),
("C004", "Dan", 'M', "Gold");
Insert into Review (ReviewID, CustomerID, MenuID, Rating, Review) Values
("R001", "C001", "M001", 5, "Perfect!"),
("R002", "C001", "M004", 2, "Too Sweet!"),
("R003", "C003", "M001", 5, "Nice"),
("R005", "C001", "M001", 4, "Good");
執行完成之後,可以先選擇左邊的 Schemas 並且選擇右上角重新整理的符號,就會發現有一個新增的資料庫,展開表格列表,會發現有四個很像在第二天的文章,解釋輕食餐廳所使用的關係表。讓我們很快速地用昨天使用到的SQL語言來看看這些關係表中有哪些資料吧。
首先我們先執行一個查詢來看菜單裡面有哪些食物:
Select * From Menu;
現在的菜單裡面總共有6個選擇,第二天我們看到的表格有一點點不一樣的地方差別在於我們新增了一個叫做食物類別的表格,讓我們來看看這個食物類別有哪些資料:
Select * from foodtype;
沒錯這個食物類別就是記錄這間店賣的食物種類。從上面的兩張關係表裡面我們可以看出在菜單上面的第一個品項和第三個品項分別對應到的食物類型都是編號 T001
的蛋糕。如果我們在查詢的時候想要把兩張表串連起來這個時候我們時就可以將多個關係表放在 SQL 中的 From
像下面這個樣子
Select *
from Menu, Foodtype;
有沒有覺得這一個查詢有一點點奇怪,沒有錯他其實就是吧上面那個表的第一行和下面那一個表的第一行結合,然後上面那一張表的第一行在雨下面那一張表的第二航結合,以此類推。這樣的東西好像不是我們要的。我們想要看到的資料表,只和下面那張表有相對應的食物種類編號 TypeID
做結合。這時候我們就寫成:
Select *
from Menu, Foodtype
Where Menu.TypeID = Foodtype.TypeID;
上面這個指令我們其實會了解到兩件事情,第一件事情是當指令在執行的時候他一定會先從 From
這一個區段開始執行,所以在這裡他會一行一行將兩個表格做結合,並在結合的當下去檢查下面 Where
的指令條件是否符合。SQL只會留下符合條件的指令。第二件我們可以從這一個指令發現的事情是,因為兩個列表中都有TypeID,所以在Where
的指令區段必須要去明確的指出是哪一個表格中的TypeID。為了讓指令長度可以再短一點,我們也可以用代號,將指令改寫,並維持相同的結果。
Select *
from Menu M, Foodtype F
Where M.TypeID = F.TypeID;
這樣的簡寫之所以可以成功作用也同樣是因為SQL在執行的過程中一定會先執行區段中的 From
,再執行區段裡的 Where
。
上面這種表格的結合方式,我們稱之為 Cross Product (交叉乘積)。左邊的表格中的每一列與右邊的表格每一列作組合。SQL 語法中有數種連接方式。再未明定的狀態下,會預設是Cross Product. 嚴格說起來,這種交叉乘積的結合方是在實務上較少使用。
最常見到的會是下面這個寫法 (輸出結果是一樣的!)
Select *
from Menu M Join Foodtype F
On M.TypeID = F.TypeID;
這個指令裡面出現了兩個新的關鍵字,一個是 Join
一個是 On
。這邊的 Join
指的是當特定值相等的情況,兩個表格中列做結合。這樣的結合方是我們叫做 Equijoin。如果正巧的,兩個關係表中,相對應的欄位名稱一樣 (像這裡我們在兩個表中都叫做 TypeID),這時候就可以使用自然鏈結 (Natural Join)
Select *
from Menu M Natural Join Foodtype F;
自然連結會把兩張表格裡面相同名稱的欄位自動配對。所以像上面那個結果會發現一件事情是原本的Type ID 只剩下一個欄位,而且在寫語法的時候並不用特別指定哪些欄位要相等。
最後我們在這個結果裡面會發現一件事情,兩個單獨的列表當中都存在有空白質的資料。仔細核對會發現菜單的關係表中,第六項的食物,因為沒有TypeID,所以就被排除在剛剛我們跑的查詢結果之外。有時候我們還是想要呈現出,即使有空白值的資料。這時候就會這樣子寫。
Select *
from Menu M Left Outer Join Foodtype F
On M.TypeID = F.TypeID;
你會發現剛剛在寫查詢的時候我將菜單放在左邊然後食物類別放在右邊,這時候當我做了Left Outer Join,他就會吧左邊擁有空白質的資料保存下來。相對的如果我現在把left outer join改成right outer join,可想而知的是我們就會保存下載食物種類表中的糖果像下面這樣子。
Select *
from Menu M Right Outer Join Foodtype F
On M.TypeID = F.TypeID;
註記一下, Left Outer Join
也有人會簡寫成 Left Join
;Right Outer Join
簡寫成 Right Join
。最後附上一張網路上 SQL Join 的圖 (來源)。