前言:之前已經針對數據庫的單表查詢進行了詳細的介紹:MySQL之增刪改查,然而實際開發中業務邏輯較為復雜,需要對多張表進行操作,現在對多表操作進行介紹。
前提:為方便后面的操作,我們首先創建一個數據庫 test,再在 test 里創建兩個數據表:grade(班級表)和student(學生表)
創建數據庫:CREATE DATABASE test;
選擇要操作的數據表:USE test;
創建數據表:CREATE TABLE grade
(
id INT(4) NOT NULL PRIMARY KEY,
name varchar(36)
);
CREATE TABLE student
(
sid INT(4) NOT NULL PRIMARY KEY,
sname VARCHAR(36),
gid INT(4) NOT NULL
);
1、外鍵
定義:外鍵是指引用另一個表中的一列或多列,被引用的列應該具有主鍵約束或唯一性約束,外鍵用於建立和加強兩個表數據之間的連接。在已經建立的grade表和student表中,student表中的gid就是grade表中的id,那么gid就可以作為student表的外鍵。其中,被引用的表grade就是主表,引用外鍵的表,即student表就是從表,兩個表示主從關系。
1.1 為表添加外鍵約束
語法:ALTER TABLE 表名 ADD CONSTRAINT 外鍵名 FOREIGN KEY(外鍵字段名) REFERENCES 外表表名(主鍵字段名);
舉例:為student表添加外鍵約束
命令:ALTER TABLE student ADD CONSTRAINT FK_ID FOREIGN KEY (gid) REFERENCES grade(id);
注意:定義外鍵名時不能加引號。
使用SHOW CREATE ABLE student命令查看student表:
結果:
說明名為“FK_ID”的外鍵已經成功添加。
注意:如果未出現此結果,需要先將 grade 表和 student 的 engine 改為 InnoDB ,命令為:ALTER TABLE grade ENGINE=InnoDB;ALTER TABLE student ENGINE=InnoDB;
1.2 刪除外鍵約束
語法:ALTER TABLE 表名 DROP FOREIGN KEY 外鍵名;
舉例:刪除student表中的外鍵約束
命令:ALTER TABLE student DROP FOREIGN KEY FK_ID;
執行刪除外鍵命令之后再使用 SHOW CREATE TABLE student;查看:
出現此結果說明外鍵已被成功刪除。
2、操作關聯表
2.1 關聯關系
(1)多對一
數據表中最常見的一種關系,比如學生與班級的關系,一個班級可以有多個學生,但是一個學生不能屬於多個班級。在多對一的關系中,應該將外鍵建在多的一方。
(2)多對多
比如學生與課程的關系,一個學生可以選擇多門課程,一門課程也供多個學生選擇
(2)一對一
比如一個人只有一張身份證,而一張身份證也只對應一個人。
2.2 添加數據
在表grade和表student中添加約束來建立兩個表的關聯關系:
ALTER TABLE student ADD CONSTRAINT FK_ID FOREIGN KEY(gid) REFERENCES grade (id);
先為主表grade添加數據:
INSERT INTO grade(id,name) VALUES (1,'軟件一班'),(2,'軟件二班');
此處若出現“1366錯誤”,可先執行如下命令:
ALTER TABLE grade CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ;
查詢插入數據后的grade表:
同理再在student表中插入數據,而且因為grade中添加的主鍵id只有1和2,所以student表中的gid字段值只能為1或者2,如下:
INSERT INTO student (sid,sname,gid) VALUES (1,'王紅',1),(2,'李強',1),(3,'趙四',2),(4,'郝娟',2);
查詢插入數據后的student表:
上述命令執行完之后,兩個表之間就具有了關聯性,假如要查詢軟件一班有哪些學生,就要先查詢軟件一班的 id ,再根據這個 id 在student表中查詢有哪些學生。
步驟一:查出grade表中軟件一班的 id :
命令:SELECT id FROM grade WHERE name='軟件一班';
結果:
步驟二:根據 id=1 ,在student表中查出對應的學生。
命令:SELECT name FROM student WHERE gid=1;
結果:
2.3 刪除數據
因為grade表和student表具有關聯關系,,而參照列的被參照值是不能被刪除的,所以若想刪除grade表中的軟件一班,必須先刪除student表中軟件一班對應的所有學生。
步驟1:刪除軟件一班所有學生
命令:DELETE FROM student WHERE sname='王紅' OR sname='李強';
或者 DELETE FROM student WHERE gid=1;
查詢:SELECT * FROM student WHERE gid=1;
結果:
說明軟件一班的學生已經全部刪除。
步驟二:在grade表中將軟件一班刪除
命令:DELETE FROM grade WHERE id=1;
查詢:SELECT * FROM grade;
結果:
可見軟件一班已被成功刪除。
現在我們來看看直接刪除軟件二班會出現什么結果:
命令:DELETE FROM grade WHERE id=2;
結果:
由此可以得出結論:在兩個具有關聯關系的表中刪除數據時,一定要先刪除從表中的數據,再刪除主表中的數據。
3、連接查詢
在進行下面的操作之前,先在test數據庫中創建兩個表:department表和employee表。
USE test;
CREATE TABLE department
(
did INT(4) NOT NULL PRIMARY KEY,
dname VARCHAR(36)
);
CREATE TABLE employee
(
id INT(4) NOT NULL PRIMARY KEY,
name VARCHAR(36),
age INT(2),
did INT(4) NOT NULL
);
再向兩個表插入數據:
INSERT INTO department(did,dname)
VALUES (1,'網絡部'),(2,'媒體部'),(3,'研發部'),(5,'人事部');
INSERT INTO employee(id,name,age,did)
VALUES (1,'王紅',20,1),(2,'李強',22,1),(3,'趙四',20,2),(4,'郝娟',20,4);
3.1 交叉連接
交叉連接返回的結果是被連接的兩個表中所有數據行的笛卡爾集,也即返回第一個表中符合查詢條件的數據行數乘以第二個表中符合查詢條件的數據行數,例如,department表中有四個部門,employee表中有四個員工,那么交叉連接的結果就有4 * 4 = 16 條數據。
交叉連接的語法:SELECT * FROM 表1 CROSS JOIN 表2;
舉例:使用交叉連接查詢department表和employee表中的所有數據
命令:SELECT * FROM department CROSS JOIN employee;
結果:
3.2 內連接
內連接(Inner Join)又稱簡單連接或自然連接,是一種常見的連接查詢。內連接使用比較運算符對兩個表中的數據進行比較,並列出與連接條件匹配的數據行,組成新的記錄。
語法:SELECT 查詢字段 FROM 表1 [ INNER ] JOIN 表2 ON 表1.關系字段=表2.關系字段
其中 INNER JOIN 用於連接兩個表,ON 來指定連接條件
舉例:在department表和employee表之間使用內連接查詢。
命令:SELECT employee.name,department.dname FROM department JOIN employee ON department.did=employee.did;
結果:
從結果可以看出,只有department.did 與employee.did 相等的員工才會被顯示。
此處還可以使用WHERE 語句來實現同樣的功能:
SELECT employee.name,department.dname FROM department,employee WHERE department.did=employee.did;
3.3 自連接
如果在一個連接查詢中涉及的兩個表其實是同一個表,這種查詢稱為自連接查詢,例如要查詢王紅所在的部門有多少個人,就可以用自連接查詢。
命令:SELECT p1.* FROM employee AS p1 JOIN employee AS p2 ON p1.did=p2.did WHERE p2.name='王紅';
結果:
從結果看來,王紅所在的部門有兩個員工,分別是王紅和李強。
3.4 外連接
外連接分為左連接和右連接,當返回的查詢結果不僅需要包含符合條件的數據,還需要包含其中一個表或者兩個表的所有數據的時候,需要用到外連接查詢。
語法:SELECT 所查字段 FROM 表1 LEFT | RIGHT [ OUTER ] JOIN 表2
ON 表1.關系字段=表2.關系字段
WHERE 條件
(1)LEFT JOIN ——左連接:返回包括左表中的所有記錄和右表中符合條件的記錄。
舉例:SELECT department.did,department.dname,employee.name FROM department LEFT JOIN employee ON department.did=employee.did;
結果:
在此結果中,department中的記錄全部顯示,而employee中只顯示了符合條件的數據(一共四條記錄,只顯示了符合條件的其中3條),因為“人事部”沒有人,所以相應的字段顯示為NULL。
(2)RIGHT JOIN ——右連接:與左連接相反,返回包括右表中的所有記錄和左表中符合條件的記錄。
舉例:SELECT department.did,department.dname,employee.name FROM department RIGHT JOIN employee ON department.did=employee.did;
結果:
在此結果中,employee中的記錄全部顯示,而department中只顯示了符合條件的數據,因為“郝娟”沒有對應部門,所以相應的字段顯示為NULL。
3.5 復合條件連接查詢
復合條件連接查詢指在連接查詢時,通過添加限制條件來過濾結果。
舉例:在department表和employee表中使用內連接查詢,並將結果按照年齡降序排列
命令:SELECT employee.name,employee.age,department.dname FROM department JOIN employee ON department.did=employee.did ORDER BY age DESC;
結果:
4、子查詢
子查詢是指一個查詢語句嵌套在另一個查詢語句內部的查詢。在執行時,首先執行子查詢中的語句,然后將返回的結果作為外層查詢的過濾條件。
4.1 帶 IN 關鍵字的子查詢
舉例1:查詢年齡為20歲的員工的部門
命令:SELECT * FROM department WHERE id IN (SELECT did FROM employee WHERE age=20);
結果:
舉例2:查詢不存在年齡為20歲的員工的部門
命令:SELECT * FROM department WHERE did NOT IN (SELECT did WHERE age=20);
結果:
4.2 帶 EXISTS 關鍵字的子查詢
EXISTS 關鍵字后面的參數可以是任意一個子查詢,這個子查詢不產生任何數據,只返回 TRUE 或 FALSE,當返回 TRUE 時,外層查詢才會執行。
舉例:查詢employee表中是否存在年齡大於21歲的員工,若存在則查詢department表中所有記錄。
命令:SELECT * FROM department WHERE EXISTS (SELECT did FROM employee WHERE age >21 );
結果:
4.3 帶 ANY 關鍵字的子查詢
ANY 關鍵字表示只要滿足內層子查詢中的任意一個條件,就返回一個結果作為外層查詢條件。
舉例:使用帶ANY 關鍵字的查詢,查詢滿足條件的部門。
命令:SELECT * FROM department WHERE did >ANY (SELECT did FROM employee);
結果:
在此命令中,子查詢會先將employee表中所有did查詢出來,分別是1,1,2,4,然后將 department 中的 did 的值與之比較,只要大於employee.did中的任意一個值,就是符合查詢條件的結果。由於employee.did的最小值為1,所以department中只要大於1的did都滿足條件,即2,3,5。
4.4 帶 ALL 關鍵字的子查詢
ALL關鍵字類似於ANY ,只是ALL關鍵字的子查詢返回的結果需要同時滿足所有內查詢條件。
舉例:使用帶 ALL 關鍵字的子查詢,查詢滿足條件的部門。
命令:SELECT * FROM department WHERE did > ALL (SELECT did FROM employee);
結果:
在此命令中,子查詢會先將employee表中所有did查詢出來,分別是1,1,2,4,然后將 department 中的 did 的值與之比較,只有大於employee.did中的所有值,才是符合查詢條件的結果。由於employee.did的最大值為4,所以department中只有大於4的did才滿足條件,即5。
5.5 帶 比較運算符的子查詢
舉例:使用帶比較運算符的子查詢,查詢趙四屬於哪個部門
命令:SELECT did,name FROM department WHERE did = (SELECT did FROM employee WHERE name='趙四');
結果: