mysql學習筆記
文章目錄
- mysql學習筆記
- 一、 安裝
- 二、連接數據庫
- 三、msql使用語法
- msql 註釋
- 1.查詢數據
- 2.查詢不重複記錄 DISTINCT(去重)
- 3.模糊查詢 LIKE
- 4. 正則查詢
- 4.子句 IN ANY ALL
- 5、範圍查詢 BETWEEN
- 6、排序 ORDER BY
- 7.添加數據
- 8.更新數據
- 9. 刪除數據
- 9、分組後的條件查詢 HAVING
- 10.WHERE 子句
- 11.創建數據庫
- 12.刪除數據庫
- 13.創建數據表
- 14. 刪除表
- 15.約束
- 16.多表查詢
- 17. 合併查詢數據 union(並集)
- 18 as別名(多表查詢防止重名)
- 19.比較同表同字段(複製表)
- 四、函數
- 1. 查詢匹配行數 COUNT
- 2. 查詢匹配數組列數 SUM
- 3. 返回數值列的平均值 AVG
- 4. 返回指定列的最大值MAX
- 5. 返回指定列的最小值 MIN
- 6. 把字段的值轉換爲大寫 UCASE
- 7. 把字段的值轉換爲小寫 LCASE
- 8. 從文本字段中提取指定字符MID
- 9.返回文本字段中值的長度LENGTH
- 10.返回當前系統的日期和時間 NOW
- 11.結果集分組GROUP BY
- 12、year()查詢返回年
- 五、連接查詢
- 六、事務
- 五、數據庫知識
- 六、查詢練習
一、 安裝
官網:https://www.mysql.com/
下載:https://dev.mysql.com/downloads/mysql/
學習安裝(只爲學習,最好百度下載下面的集成工具,不要去官網下,太慢了)
(XAMMP,集成管理工具)免去數據庫安裝的漫長如果下載過慢,可以百度一下綠色軟件
XAMPP:https://www.apachefriends.org/index.html 注意:安裝路徑不要出現中文空格等字符
MySQL特點
- 數據以表格的形式出現
表格中每一行表示一組數據
表格中每一列表示某組數據對應的字段(屬性)
若干這樣的行和列就組成了一張表 - 若干個表格組成一個庫
二、連接數據庫
注意:如果需要使用MySQL,需要首先啓動MySQL服務器
連接MySQL
-
通過 CLI 進行連接管理數據
進入XAMPP安裝目錄找到mysql文件夾,進入找到bin文件 啓動cmd 執行命令
./mysql -u root -p 登陸本機
-
-h : 指定客戶端所要登錄的 MySQL 主機名, 登錄本機(localhost 或 127.0.0.1)該參數可以省略;
-
-u : 登錄的用戶名;
-
-p : 告訴服務器將會使用一個密碼來登錄, 如果所要登錄的用戶名密碼爲空, 可以忽略此選項。
密碼默認爲空回車就行
-
show databases; 查看所有數據庫(不要再默認庫中操作,test除外)
create database dbName; 創建數據庫
use test ; 選擇要操作的數據庫
show tables; 查看存在的表
select * from 表名;查看錶的數據 (where 查看條件)
```mysql
通過 GUI 程序進行連接管理數據庫
下載 [nvicat ](http://www.downcc.com/soft/322714.html )
```
通過程序(Node.js、PHP、Java……)提供的 API 連接管理數據庫
三、msql使用語法
msql 註釋
註釋是 -- 後面還有個空格
1.查詢數據
SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[LIMIT N][ OFFSET M]
第二種寫法
SELECT column_name,column_name
FROM table_name
[LIMIT [M, ]N]
注意,這種寫法偏移在前,限制在後,如果只有一個數字,默認爲限制
數據查詢 - 多表查詢
SELECT * FROM 表一, 表二 WHERE 表一.字段 運算符 表二.字段
數據查詢 - 內連接(同上)
SELECT * FROM 表一 JOIN 表二 ON 表一.字段 運算符 表二.字段
SELECT * FROM 表一 INNER JOIN 表二 ON 表一.字段 運算符 表二.字段
數據查詢 - 左連接
SELECT 字段 FROM 表一 LEFT JOIN 表二
ON 表一.字段 運算符 表二.字段
LEFT JOIN 關鍵字從左表(表一)返回所有的行,即使右表(表二)中沒有匹配。如果右表中沒有匹配,則結果爲 NULL。
數據查詢 - 右連接
SELECT 字段 FROM 表一 RIGHT JOIN 表二
ON 表一.字段 運算符 表二.字段
RIGHT JOIN 關鍵字從右表(表二)返回所有的行,即使左表(表一)中沒有匹配。如果左表中沒有匹配,則結果爲 NULL。
- 查詢語句中你可以使用一個或者多個表,表之間使用逗號(,)分割,並使用WHERE語句來設定查詢條件。
- SELECT 命令可以讀取一條或者多條記錄。
- 你可以使用星號(*)來代替其他字段,SELECT語句會返回表的所有字段數據
- 你可以使用 WHERE 語句來包含任何條件。
- 你可以使用 LIMIT 屬性來設定返回的記錄數。
- 你可以通過OFFSET指定SELECT語句開始查詢的數據偏移量。默認情況下偏移量爲0。
- OFFSET必須與LIMIT一起使用,且LIMIT在前
2.查詢不重複記錄 DISTINCT(去重)
DISTINCT
查詢表中不重複的記錄,如果指定多個字段,則作爲聯合條件
SELECT DISTINCT column_name,column_name... FROM table_name
3.模糊查詢 LIKE
LIKE
模糊查詢,通常與 % 配合使用,不使用 % 同 =
% 類似 * 的作用,通配
%mm:以 mm 結尾的內容
mm%:以 mm 開頭的內容
%mm%:包含 mm 的內容
SELECT column_name... FROM table_name WHERE column_name LIKE %mm%
NOT LIKE:與 LIKE 相反
- 你可以在 WHERE 子句中指定任何條件。
- 你可以在 WHERE 子句中使用LIKE子句。
- 你可以使用LIKE子句代替等號 =。
- LIKE 通常與 % 一同使用,類似於一個元字符的搜索。
- 你可以使用 AND 或者 OR 指定一個或多個條件。
- 你可以在 DELETE 或 UPDATE 命令中使用 WHERE…LIKE 子句來指定條件。
4. 正則查詢
數據查詢 - 模糊查詢 - 正則
SELECT 字段... FROM 表名 WHERE 字段名 REGEXP '規則'
規則:正則表達式
注意:字符串轉義,\d 需要寫成 '\\d'
4.子句 IN ANY ALL
IN
多值匹配
SELECT column_name... FROM table_name WHERE column_name IN (value1, value2...)
NOT IN (not in 是 “<>all”的別名)
與 IN 相反
ANY (in 與“=any”是相同的)
至少 對於子查詢返回的列中的任一數值,如果比較結果爲true,則返回true
select s1 from t1 where s1 > any (select s1 from t2);
All
all的意思是“對於子查詢返回的列中的所有值,如果比較結果爲true,則返回true”
5、範圍查詢 BETWEEN
BETWEEN
範圍查詢
SELECT column_name... FROM table_name WHERE column_name BETWEEN value1 AND value2
NOT BETWEEN
與 BETWEEN 相反
6、排序 ORDER BY
按照某個字段某種規則進行排序
SELECT column_name... FROM table_name ORDER BY column_name1 DESC, column_name2 ASC
DESC:降序
ASC:升序,默認
- 如果有多個排序字段和規則,執行順序爲從做到右
- ORDER BY 必須在 LIMIT 之前 WHERE(GROUP BY) 之後
7.添加數據
INSERT INTO table_name ( field1, field2,...fieldN )
VALUES
( value1, value2,...valueN );
返回值依據其操作來決定
- INSERT INTO:[{affectedRows ,insertId},undefined]
affectedRows可判斷是否插入成功,表示插入的條數
如果數據是字符型,必須使用單引號或者雙引號,如:“value”。
8.更新數據
UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]
返回值依據其操作來決定
- UPDATE:[{affectedRows ,insertId},undefined]
- 你可以同時更新一個或多個字段。
- 你可以在 WHERE 子句中指定任何條件。
- 你可以在一個單獨表中同時更新數據
9. 刪除數據
DELETE FROM table_name [WHERE Clause]
返回值依據其操作來決定
- INSERT INTO:[{affectedRows},undefined]
- 如果沒有指定 WHERE 子句,MySQL 表中的所有記錄將被刪除。
- 你可以在 WHERE 子句中指定任何條件
- 您可以在單個表中一次性刪除記錄。
9、分組後的條件查詢 HAVING
SELECT column_name... FROM table_name GROUP BY column_name1 HAVING condition1 [AND [OR]] condition2.....
where適用於分組前的查詢,having用於分組之後的查詢
10.WHERE 子句
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
[WHERE condition1 [AND [OR]] condition2.....
-
查詢語句中你可以使用一個或者多個表,表之間使用逗號, 分割,並使用WHERE語句來設定查詢條件。
-
你可以在 WHERE 子句中指定任何條件。
-
你可以使用 AND 或者 OR 指定一個或多個條件。
-
WHERE 子句也可以運用於 SQL 的 DELETE 或者 UPDATE 命令。
-
WHERE 子句類似於程序語言中的 if 條件,根據 MySQL 表中的字段值來讀取指定的數據。
操作符 | 描述 | 實例 |
---|---|---|
= | 等號,檢測兩個值是否相等,如果相等返回true | (A = B) 返回false。 |
<>, != | 不等於,檢測兩個值是否相等,如果不相等返回true | (A != B) 返回 true。 |
> | 大於號,檢測左邊的值是否大於右邊的值, 如果左邊的值大於右邊的值返回true | (A > B) 返回false。 |
< | 小於號,檢測左邊的值是否小於右邊的值, 如果左邊的值小於右邊的值返回true | (A < B) 返回 true。 |
>= | 大於等於號,檢測左邊的值是否大於或等於右邊的值, 如果左邊的值大於或等於右邊的值返回true | (A >= B) 返回false。 |
<= | 小於等於號,檢測左邊的值是否小於或等於右邊的值, 如果左邊的值小於或等於右邊的值返回true | (A <= B) 返回 true。 |
11.創建數據庫
CREATE DATABASE 數據庫名稱
CREATE DATABASE 數據庫名稱 DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_bin
12.刪除數據庫
drop database <數據庫名>;
13.創建數據表
CREATE TABLE table_name (column_name column_type);
CREATE TABLE 數據庫表名稱 (
字段名稱 字段屬性...,
PRIMARY KEY (主鍵字段名稱),
INDEX 索引名稱(索引字段...)...
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
`runoob_id` INT UNSIGNED AUTO_INCREMENT,
`runoob_title` VARCHAR(100) NOT NULL,
`runoob_author` VARCHAR(40) NOT NULL,
`submission_date` DATE,
PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
字段屬性設置
字段類型:int(10)、char(10)、varchar(200)
是否爲null:NOT NULL
無符號:UNSIGNED
自動增長:AUTO_INCREMENT
默認值:DEFAULT 0
- 如果你不想字段爲 NULL 可以設置字段的屬性爲 NOT NULL, 在操作數據庫時如果輸入該字段的數據爲NULL ,就會報錯。
- AUTO_INCREMENT定義列爲自增的屬性,一般用於主鍵,數值會自動加1。
- PRIMARY KEY關鍵字用於定義列爲主鍵。 您可以使用多列來定義主鍵,列間以逗號分隔。
- ENGINE 設置存儲引擎,CHARSET 設置編碼。
14. 刪除表
整表刪除
DELETE FROM 表名稱
刪除表的其他方法
DROP 表名稱:刪除表、數據以及結構
TRUNCATE 表名稱:刪除表的數據,保留結構,不支持事務,不可撤銷恢復
DELETE 表名稱:刪除表的數據, 保留結構,支持事務
15.約束
-- 主鍵約束
-- 使某個字段不重複且不得爲空,確保表內所有數據的唯一性。
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20)
);
-- 聯合主鍵
-- 聯合主鍵中的每個字段都不能爲空,並且加起來值不能和已設置的聯合主鍵重複。
CREATE TABLE user (
id INT,
name VARCHAR(20),
password VARCHAR(20),
PRIMARY KEY(id, name)
);
例:
id 1 name zhangsan 和id 2 name zhangsan
-- 自增約束
-- 自增約束的主鍵由系統自動遞增分配。
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
-- 添加主鍵約束
-- 如果忘記設置主鍵,還可以通過SQL語句設置(兩種方式):
ALTER TABLE user ADD PRIMARY KEY(id);
ALTER TABLE user MODIFY id INT PRIMARY KEY;
-- 刪除主鍵
ALTER TABLE user drop PRIMARY KEY;
-- 建表時創建唯一主鍵
CREATE TABLE user (
id INT,
name VARCHAR(20),
UNIQUE(name)
);
-- 添加唯一主鍵
-- 如果建表時沒有設置唯一建,還可以通過SQL語句設置(兩種方式):
ALTER TABLE user ADD UNIQUE(name);
ALTER TABLE user MODIFY name VARCHAR(20) UNIQUE;
-- 刪除唯一主鍵
ALTER TABLE user DROP INDEX name;
非空約束
-- 約束某個字段不能爲空
CREATE TABLE user (
id INT,
name VARCHAR(20) NOT NULL
);
-- 移除非空約束
ALTER TABLE user MODIFY name VARCHAR(20);
-- 建表時添加默認約束
-- 約束某個字段的默認值
CREATE TABLE user2 (
id INT,
name VARCHAR(20),
age INT DEFAULT 10
);
-- 移除非空約束
ALTER TABLE user MODIFY age INT;
-- 外鍵約束(不常用,一般會用內連接查詢,定義外鍵容易導致表無法刪除,基本不用)
-- 班級
CREATE TABLE classes (
id INT PRIMARY KEY,
name VARCHAR(20)
);
-- 學生表
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(20),
-- 這裏的 class_id 要和 classes 中的 id 字段相關聯
class_id INT,
-- 表示 class_id 的值必須來自於 classes 中的 id 字段值
FOREIGN KEY(class_id) REFERENCES classes(id)
);
-- 1. 主表(父表)classes 中沒有的數據值,在副表(子表)students 中,是不可以使用的;
-- 2. 主表中的記錄被副表引用時,主表不可以被刪除。
16.多表查詢
SELECT column_name,column_name FROM table_name,table_name WHERE p.id = c.id
需要外鍵約束,父表和子表
17. 合併查詢數據 union(並集)
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT]
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];
UNION 語句:用於將不同表中相同列中查詢的數據展示出來;(不包括重複數據)
UNION ALL 語句:用於將不同表中相同列中查詢的數據展示出來;(包括重複數據)
SELECT 列名稱 FROM 表名稱 UNION SELECT 列名稱 FROM 表名稱 ORDER BY 列名稱;
SELECT 列名稱 FROM 表名稱 UNION ALL SELECT 列名稱 FROM 表名稱 ORDER BY 列名稱
- expression1, expression2, … expression_n: 要檢索的列。
- tables: 要檢索的數據表。
- WHERE conditions: 可選, 檢索條件。
- DISTINCT: 可選,刪除結果集中重複的數據。默認情況下 UNION 操作符已經刪除了重複數據,所以 DISTINCT 修飾符對結果沒啥影響。
- ALL: 可選,返回所有結果集,包含重複數據。
18 as別名(多表查詢防止重名)
SELECT cloumn_name as name FROM tables
19.比較同表同字段(複製表)
-- 將表 b 作用於表 a 中查詢數據
-- score a (b): 將表聲明爲 a (b),
-- 如此就能用 a.c_no = b.c_no 作爲條件執行查詢了。
SELECT * FROM score a WHERE degree < ( (SELECT AVG(degree) FROM score b WHERE a.c_no = b.c_no))
四、函數
SQL 也提供了一些內置函數,以便對數據進行一些常規操作
-
聚合函數
-
計算從列中取得的值,返回一個單一的值,如:COUNT、SUM、MAX、MIN
標量函數
-
基於輸入值,返回一個單一的值,如:UCASE、LCASE、NOW
-
1. 查詢匹配行數 COUNT
COUNT()
返回匹配指定條件的行數
SELECT COUNT(column_name) FROM table_name
2. 查詢匹配數組列數 SUM
SUM()
返回數值列的總數
SELECT SUM(column_name) FROM table_name
3. 返回數值列的平均值 AVG
返回數值列的平均值
SELECT AVG(column_name) FROM table_name
分組適用
4. 返回指定列的最大值MAX
返回指定列的最大值
SELECT MAX(column_name) FROM table_name
5. 返回指定列的最小值 MIN
返回指定列的最小值
SELECT MIN(column_name) FROM table_name
6. 把字段的值轉換爲大寫 UCASE
把字段的值轉換爲大寫
SELECT UCASE(column_name) FROM table_name
可以和 where形成複合語句
SELECT s_no, c_no FROM score WHERE degree = (SELECT MAX(degree) FROM score);
7. 把字段的值轉換爲小寫 LCASE
把字段的值轉換爲小寫
SELECT LCASE(column_name) FROM table_name
8. 從文本字段中提取指定字符MID
從文本字段中提取指定字符
SELECT MID(column_name,start[,length]) FROM table_name
start:從1開始計算
9.返回文本字段中值的長度LENGTH
返回文本字段中值的長度
SELECT LENGTH(column_name) FROM table_name
10.返回當前系統的日期和時間 NOW
SELECT NOW() FROM table_name
11.結果集分組GROUP BY
用於結合聚合函數,根據一個或多個列對結果集進行分組
SELECT column_name... FROM table_name GROUP BY column_name1
如果有多個字段,則爲 AND
12、year()查詢返回年
SELECT year(cloumn_name) FROM table_name
五、連接查詢
準備用於測試連接查詢的數據:
CREATE DATABASE testJoin;
CREATE TABLE person (
id INT,
name VARCHAR(20),
cardId INT
);
CREATE TABLE card (
id INT,
name VARCHAR(20)
);
INSERT INTO card VALUES (1, '飯卡'), (2, '建行卡'), (3, '農行卡'), (4, '工商卡'), (5, '郵政卡');
SELECT * FROM card;
+------+-----------+
| id | name |
+------+-----------+
| 1 | 飯卡 |
| 2 | 建行卡 |
| 3 | 農行卡 |
| 4 | 工商卡 |
| 5 | 郵政卡 |
+------+-----------+
INSERT INTO person VALUES (1, '張三', 1), (2, '李四', 3), (3, '王五', 6);
SELECT * FROM person;
+------+--------+--------+
| id | name | cardId |
+------+--------+--------+
| 1 | 張三 | 1 |
| 2 | 李四 | 3 |
| 3 | 王五 | 6 |
+------+--------+--------+
分析兩張表發現,person
表並沒有爲 cardId
字段設置一個在 card
表中對應的 id
外鍵。如果設置了的話,person
中 cardId
字段值爲 6
的行就插不進去,因爲該 cardId
值在 card
表中並沒有。
1. 內連接(常用,)
要查詢這兩張表中有關係的數據,可以使用 INNER JOIN
( 內連接 ) 將它們連接在一起。
-- INNER JOIN: 表示爲內連接,將兩張表拼接在一起。
-- on: 表示要執行某個條件。(連接條件)
SELECT * FROM person INNER JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 張三 | 1 | 1 | 飯卡 |
| 2 | 李四 | 3 | 3 | 農行卡 |
+------+--------+--------+------+-----------+
-- 將 INNER 關鍵字省略掉,結果也是一樣的。
-- SELECT * FROM person JOIN card on person.cardId = card.id;
注意:
card
的整張表被連接到了右邊。
2、左外連接
完整顯示左邊的表 ( person
) ,右邊的表如果符合條件就顯示,不符合則補 NULL
。
-- LEFT JOIN 也叫做 LEFT OUTER JOIN,用這兩種方式的查詢結果是一樣的。
SELECT * FROM person LEFT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 張三 | 1 | 1 | 飯卡 |
| 2 | 李四 | 3 | 3 | 農行卡 |
| 3 | 王五 | 6 | NULL | NULL |
+------+--------+--------+------+-----------+
3、右外鏈接
完整顯示右邊的表 ( card
) ,左邊的表如果符合條件就顯示,不符合則補 NULL
。
SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 張三 | 1 | 1 | 飯卡 |
| 2 | 李四 | 3 | 3 | 農行卡 |
| NULL | NULL | NULL | 2 | 建行卡 |
| NULL | NULL | NULL | 4 | 工商卡 |
| NULL | NULL | NULL | 5 | 郵政卡 |
+------+--------+--------+------+-----------+
4、全外鏈接
完整顯示兩張表的全部數據。
-- MySQL 不支持這種語法的全外連接
-- SELECT * FROM person FULL JOIN card on person.cardId = card.id;
-- 出現錯誤:
-- ERROR 1054 (42S22): Unknown column 'person.cardId' in 'on clause'
-- MySQL全連接語法,使用 UNION 將兩張表合併在一起。
SELECT * FROM person LEFT JOIN card on person.cardId = card.id
UNION
SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 張三 | 1 | 1 | 飯卡 |
| 2 | 李四 | 3 | 3 | 農行卡 |
| 3 | 王五 | 6 | NULL | NULL |
| NULL | NULL | NULL | 2 | 建行卡 |
| NULL | NULL | NULL | 4 | 工商卡 |
| NULL | NULL | NULL | 5 | 郵政卡 |
+------+--------+--------+------+-----------+
六、事務
在 MySQL 中,事務其實是一個最小的不可分割的工作單元。事務能夠保證一個業務的完整性。
比如我們的銀行轉賬:
-- a -> -100
UPDATE user set money = money - 100 WHERE name = 'a';
-- b -> +100
UPDATE user set money = money + 100 WHERE name = 'b';
在實際項目中,假設只有一條 SQL 語句執行成功,而另外一條執行失敗了,就會出現數據前後不一致。
因此,在執行多條有關聯 SQL 語句時,事務可能會要求這些 SQL 語句要麼同時執行成功,要麼就都執行失敗。
1、如何控制事務 - COMMIT / ROLLBACK
在 MySQL 中,事務的自動提交狀態默認是開啓的。
-- 查詢事務的自動提交狀態
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 1 |
+--------------+
自動提交的作用:當我們執行一條 SQL 語句的時候,其產生的效果就會立即體現出來,且不能回滾。
什麼是回滾?舉個例子:
CREATE DATABASE bank;
USE bank;
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20),
money INT
);
INSERT INTO user VALUES (1, 'a', 1000);
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
可以看到,在執行插入語句後數據立刻生效,原因是 MySQL 中的事務自動將它提交到了數據庫中。那麼所謂回滾的意思就是,撤銷執行過的所有 SQL 語句,使其回滾到最後一次提交數據時的狀態。
在 MySQL 中使用 ROLLBACK
執行回滾:
-- 回滾到最後一次提交
ROLLBACK;
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
由於所有執行過的 SQL 語句都已經被提交過了,所以數據並沒有發生回滾。那如何讓數據可以發生回滾?
-- 關閉自動提交
SET AUTOCOMMIT = 0;
-- 查詢自動提交狀態
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 0 |
+--------------+
將自動提交關閉後,測試數據回滾:
INSERT INTO user VALUES (2, 'b', 1000);
-- 關閉 AUTOCOMMIT 後,數據的變化是在一張虛擬的臨時數據表中展示,
-- 發生變化的數據並沒有真正插入到數據表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
-- 數據表中的真實數據其實還是:
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
-- 由於數據還沒有真正提交,可以使用回滾
ROLLBACK;
-- 再次查詢
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
那如何將虛擬的數據真正提交到數據庫中?使用 COMMIT
:
INSERT INTO user VALUES (2, 'b', 1000);
-- 手動提交數據(持久性),
-- 將數據真正提交到數據庫中,執行後不能再回滾提交過的數據。
COMMIT;
-- 提交後測試回滾
ROLLBACK;
-- 再次查詢(回滾無效了)
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
總結
自動提交
- 查看自動提交狀態:
SELECT @@AUTOCOMMIT
;- 設置自動提交狀態:
SET AUTOCOMMIT = 0
。手動提交
@@AUTOCOMMIT = 0
時,使用COMMIT
命令提交事務。事務回滾
@@AUTOCOMMIT = 0
時,使用ROLLBACK
命令回滾事務。
事務的實際應用,讓我們再回到銀行轉賬項目:
-- 轉賬
UPDATE user set money = money - 100 WHERE name = 'a';
-- 到賬
UPDATE user set money = money + 100 WHERE name = 'b';
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
這時假設在轉賬時發生了意外,就可以使用 ROLLBACK
回滾到最後一次提交的狀態:
-- 假設轉賬發生了意外,需要回滾。
ROLLBACK;
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
這時我們又回到了發生意外之前的狀態,也就是說,事務給我們提供了一個可以反悔的機會。假設數據沒有發生意外,這時可以手動將數據真正提交到數據表中:COMMIT
。
2、手動開啓事務 - BEGIN / START TRANSACTION
事務的默認提交被開啓 ( @@AUTOCOMMIT = 1
) 後,此時就不能使用事務回滾了。但是我們還可以手動開啓一個事務處理事件,使其可以發生回滾:
-- 使用 BEGIN 或者 START TRANSACTION 手動開啓一個事務
-- START TRANSACTION;
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';
-- 由於手動開啓的事務沒有開啓自動提交,
-- 此時發生變化的數據仍然是被保存在一張臨時表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
-- 測試回滾
ROLLBACK;
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
仍然使用 COMMIT
提交數據,提交後無法再發生本次事務的回滾。
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
-- 提交數據
COMMIT;
-- 測試回滾(無效,因爲表的數據已經被提交)
ROLLBACK;
3、事務的 ACID 特徵與使用
事務的四大特徵:
- A 原子性:事務是最小的單位,不可以再分割;
- C 一致性:要求同一事務中的 SQL 語句,必須保證同時成功或者失敗;
- I 隔離性:事務1 和 事務2 之間是具有隔離性的;
- D 持久性:事務一旦結束 (
COMMIT
) ,就不可以再返回了 (ROLLBACK
) 。
4、事務的隔離性
事務的隔離性可分爲四種 ( 性能從低到高 ) :
-
READ UNCOMMITTED ( 讀取未提交 )
如果有多個事務,那麼任意事務都可以看見其他事務的未提交數據。
-
READ COMMITTED ( 讀取已提交 )
只能讀取到其他事務已經提交的數據。
-
REPEATABLE READ ( 可被重複讀 )
如果有多個連接都開啓了事務,那麼事務之間不能共享數據記錄,否則只能共享已提交的記錄。
-
SERIALIZABLE ( 串行化 )
所有的事務都會按照固定順序執行,執行完一個事務後再繼續執行下一個事務的寫入操作。
查看當前數據庫的默認隔離級別:
-- MySQL 8.x, GLOBAL 表示系統級別,不加表示會話級別。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
SELECT @@TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ | -- MySQL的默認隔離級別,可以重複讀。
+--------------------------------+
-- MySQL 5.x
SELECT @@GLOBAL.TX_ISOLATION;
SELECT @@TX_ISOLATION;
修改隔離級別:
-- 設置系統隔離級別,LEVEL 後面表示要設置的隔離級別 (READ UNCOMMITTED)。
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 查詢系統隔離級別,發現已經被修改。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-UNCOMMITTED |
+--------------------------------+
1.髒讀
測試 READ UNCOMMITTED ( 讀取未提交 ) 的隔離性:
INSERT INTO user VALUES (3, '小明', 1000);
INSERT INTO user VALUES (4, '淘寶店', 1000);
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
+----+-----------+-------+
-- 開啓一個事務操作數據
-- 假設小明在淘寶店買了一雙800塊錢的鞋子:
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘寶店';
-- 然後淘寶店在另一方查詢結果,發現錢已到賬。
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 200 |
| 4 | 淘寶店 | 1800 |
+----+-----------+-------+
由於小明的轉賬是在新開啓的事務上進行操作的,而該操作的結果是可以被其他事務(另一方的淘寶店)看見的,因此淘寶店的查詢結果是正確的,淘寶店確認到賬。但就在這時,如果小明在它所處的事務上又執行了 ROLLBACK
命令,會發生什麼?
-- 小明所處的事務
ROLLBACK;
-- 此時無論對方是誰,如果再去查詢結果就會發現:
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
+----+-----------+-------+
這就是所謂的髒讀,一個事務讀取到另外一個事務還未提交的數據。這在實際開發中是不允許出現的。
2.讀取已提交
把隔離級別設置爲 READ COMMITTED :
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-COMMITTED |
+--------------------------------+
這樣,再有新的事務連接進來時,它們就只能查詢到已經提交過的事務數據了。但是對於當前事務來說,它們看到的還是未提交的數據,例如:
-- 正在操作數據事務(當前事務)
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘寶店';
-- 雖然隔離級別被設置爲了 READ COMMITTED,但在當前事務中,
-- 它看到的仍然是數據表中臨時改變數據,而不是真正提交過的數據。
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 200 |
| 4 | 淘寶店 | 1800 |
+----+-----------+-------+
-- 假設此時在遠程開啓了一個新事務,連接到數據庫。
$ mysql -u root -p12345612
-- 此時遠程連接查詢到的數據只能是已經提交過的
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
+----+-----------+-------+
但是這樣還有問題,那就是假設一個事務在操作數據時,其他事務干擾了這個事務的數據。例如:
-- 小張在查詢數據的時候發現:
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 200 |
| 4 | 淘寶店 | 1800 |
+----+-----------+-------+
-- 在小張求表的 money 平均值之前,小王做了一個操作:
START TRANSACTION;
INSERT INTO user VALUES (5, 'c', 100);
COMMIT;
-- 此時表的真實數據是:
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
| 5 | c | 100 |
+----+-----------+-------+
-- 這時小張再求平均值的時候,就會出現計算不相符合的情況:
SELECT AVG(money) FROM user;
+------------+
| AVG(money) |
+------------+
| 820.0000 |
+------------+
雖然 READ COMMITTED 讓我們只能讀取到其他事務已經提交的數據,但還是會出現問題,就是在讀取同一個表的數據時,可能會發生前後不一致的情況。這被稱爲不可重複讀現象 ( READ COMMITTED ) 。
3、幻讀
將隔離級別設置爲 REPEATABLE READ ( 可被重複讀取 ) :
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
測試 REPEATABLE READ ,假設在兩個不同的連接上分別執行 START TRANSACTION
:
-- 小張 - 成都
START TRANSACTION;
INSERT INTO user VALUES (6, 'd', 1000);
-- 小王 - 北京
START TRANSACTION;
-- 小張 - 成都
COMMIT;
當前事務開啓後,沒提交之前,查詢不到,提交後可以被查詢到。但是,在提交之前其他事務被開啓了,那麼在這條事務線上,就不會查詢到當前有操作事務的連接。相當於開闢出一條單獨的線程。
無論小張是否執行過 COMMIT
,在小王這邊,都不會查詢到小張的事務記錄,而是隻會查詢到自己所處事務的記錄:
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
| 5 | c | 100 |
+----+-----------+-------+
這是因爲小王在此之前開啓了一個新的事務 ( START TRANSACTION ) ,那麼在他的這條新事務的線上,跟其他事務是沒有聯繫的,也就是說,此時如果其他事務正在操作數據,它是不知道的。
然而事實是,在真實的數據表中,小張已經插入了一條數據。但是小王此時並不知道,也插入了同一條數據,會發生什麼呢?
INSERT INTO user VALUES (6, 'd', 1000);
-- ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'
報錯了,操作被告知已存在主鍵爲 6
的字段。這種現象也被稱爲幻讀,一個事務提交的數據,不能被其他事務讀取到。
4、串行化
顧名思義,就是所有事務的寫入操作全都是串行化的。什麼意思?把隔離級別修改成 SERIALIZABLE :
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| SERIALIZABLE |
+--------------------------------+
還是拿小張和小王來舉例:
-- 小張 - 成都
START TRANSACTION;
-- 小王 - 北京
START TRANSACTION;
-- 開啓事務之前先查詢表,準備操作數據。
SELECT * FROM user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘寶店 | 1000 |
| 5 | c | 100 |
| 6 | d | 1000 |
+----+-----------+-------+
-- 發現沒有 7 號王小花,於是插入一條數據:
INSERT INTO user VALUES (7, '王小花', 1000);
此時會發生什麼呢?由於現在的隔離級別是 SERIALIZABLE ( 串行化 ) ,串行化的意思就是:假設把所有的事務都放在一個串行的隊列中,那麼所有的事務都會按照固定順序執行,執行完一個事務後再繼續執行下一個事務的寫入操作 ( 這意味着隊列中同時只能執行一個事務的寫入操作 ) 。
根據這個解釋,小王在插入數據時,會出現等待狀態,直到小張執行 COMMIT
結束它所處的事務,或者出現等待超時。
五、數據庫知識
參考:
存儲引擎:https://dev.mysql.com/doc/refman/5.7/en/storage-engine-setting.html
字符集、編碼:https://dev.mysql.com/doc/refman/5.7/en/charset.html
數據類型:https://dev.mysql.com/doc/refman/5.7/en/data-types.html
主鍵:https://dev.mysql.com/doc/refman/5.7/en/primary-key-optimization.html
自動增長:https://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html
索引:https://dev.mysql.com/doc/refman/5.7/en/column-indexes.html
1.字符集、編碼
字符集、編碼
指數據庫存儲的數據的編碼
- utf8mb4:支持更多的unicode字符(四字節)
數據校對
數據庫除了要存儲數據,還要對數據進行排序,比較等操作,不同的校對規則會有不同的結果
- utf8mb4_unicode_ci:基於標準的Unicode來排序和比較,能夠在各種語言之間精確排序
字符集、編碼
_bin、_cs:區分大小寫
_ci:不區分大小寫
2. 數據類型
數據存儲的類型
數字類型:INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT, DECIMAL, NUMERIC, FLOAT, DOUBLE
日期時間類型:DATE, DATETIME, TIMESTAMP, TIM, YEAR
字符串類型:CHAR, VARCHAR, BINARY, VARBINARY, BLOB, TEXT, ENUM, SET
總結起來,有幾點:
經常變化的字段用 varchar
知道固定長度的用 char
儘量用 varchar
超過 255 字符的只能用 varchar 或者 text
能用 varchar 的地方不用 text
六、查詢練習
準備數據
-- 創建數據庫
CREATE DATABASE select_test DEFAULT CHARSET utf8;
-- 切換數據庫
USE select_test;
-- 創建學生表s
學生表:
student
學號
姓名
性別
出生日期
所在班級
CREATE TABLE student (
no VARCHAR(20) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
sex VARCHAR(10) NOT NULL,
birthday DATE, -- 生日
class VARCHAR(20) -- 所在班級
);
-- 創建教師表
教師表
teacher
教師編號
教師名字
教師性別
出生日期
職稱
所在部門
CREATE TABLE teacher (
no VARCHAR(20) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
sex VARCHAR(10) NOT NULL,
birthday DATE,
profession VARCHAR(20) NOT NULL, -- 職稱
department VARCHAR(20) NOT NULL -- 部門
);
-- 創建課程表
課程表:
course
課程號
課程課程名稱
教師編號
CREATE TABLE course (
no VARCHAR(20) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
t_no VARCHAR(20) NOT NULL, -- 教師編號
-- 表示該 tno 來自於 teacher 表中的 no 字段值
FOREIGN KEY(t_no) REFERENCES teacher(no)
);
-- 成績表
成績表:
srore
學號
課程號
成績
CREATE TABLE score (
s_no VARCHAR(20) NOT NULL, -- 學生編號
c_no VARCHAR(20) NOT NULL, -- 課程號
degree DECIMAL, -- 成績
-- 表示該 s_no, c_no 分別來自於 student, course 表中的 no 字段值
FOREIGN KEY(s_no) REFERENCES student(no),
FOREIGN KEY(c_no) REFERENCES course(no),
-- 設置 s_no, c_no 爲聯合主鍵
PRIMARY KEY(s_no, c_no)
);
-- 查看所有表
SHOW TABLES;
-- 添加學生表數據
INSERT INTO student VALUES('101', '曾華', '男', '1977-09-01', '95033');
INSERT INTO student VALUES('102', '匡明', '男', '1975-10-02', '95031');
INSERT INTO student VALUES('103', '王麗', '女', '1976-01-23', '95033');
INSERT INTO student VALUES('104', '李軍', '男', '1976-02-20', '95033');
INSERT INTO student VALUES('105', '王芳', '女', '1975-02-10', '95031');
INSERT INTO student VALUES('106', '陸軍', '男', '1974-06-03', '95031');
INSERT INTO student VALUES('107', '王尼瑪', '男', '1976-02-20', '95033');
INSERT INTO student VALUES('108', '張全蛋', '男', '1975-02-10', '95031');
INSERT INTO student VALUES('109', '趙鐵柱', '男', '1974-06-03', '95031');
-- 添加教師表數據
INSERT INTO teacher VALUES('804', '李誠', '男', '1958-12-02', '副教授', '計算機系');
INSERT INTO teacher VALUES('856', '張旭', '男', '1969-03-12', '講師', '電子工程系');
INSERT INTO teacher VALUES('825', '王萍', '女', '1972-05-05', '助教', '計算機系');
INSERT INTO teacher VALUES('831', '劉冰', '女', '1977-08-14', '助教', '電子工程系');
-- 添加課程表數據
INSERT INTO course VALUES('3-105', '計算機導論', '825');
INSERT INTO course VALUES('3-245', '操作系統', '804');
INSERT INTO course VALUES('6-166', '數字電路', '856');
INSERT INTO course VALUES('9-888', '高等數學', '831');
-- 添加添加成績表數據
INSERT INTO score VALUES('103', '3-105', '92');
INSERT INTO score VALUES('103', '3-245', '86');
INSERT INTO score VALUES('103', '6-166', '85');
INSERT INTO score VALUES('105', '3-105', '88');
INSERT INTO score VALUES('105', '3-245', '75');
INSERT INTO score VALUES('105', '6-166', '79');
INSERT INTO score VALUES('109', '3-105', '76');
INSERT INTO score VALUES('109', '3-245', '68');
INSERT INTO score VALUES('109', '6-166', '81');
-- 查看錶結構
SELECT * FROM course;
SELECT * FROM score;
SELECT * FROM student;
SELECT * FROM teacher;
習題練習
-
查詢 student 表的所有行
select * from `student`; +-----+--------+------+------------+-------+ | no | name | sex | birthday | class | +-----+--------+------+------------+-------+ | 101 | 曾華 | 男 | 1977-09-01 | 95033 | | 102 | 匡明 | 男 | 1975-10-02 | 95031 | | 103 | 王麗 | 女 | 1976-01-23 | 95033 | | 104 | 李軍 | 男 | 1976-02-20 | 95033 | | 105 | 王芳 | 女 | 1975-02-10 | 95031 | | 106 | 陸軍 | 男 | 1974-06-03 | 95031 | | 107 | 王尼瑪 | 男 | 1976-02-20 | 95033 | | 108 | 張全蛋 | 男 | 1975-02-10 | 95031 | | 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 | +-----+--------+------+------------+-------+ 9 rows in set (0.03 sec)
2.查詢 student 表中的 name、sex 和 class 字段的所有行
+--------+------+-------+ | name | sex | class | +--------+------+-------+ | 曾華 | 男 | 95033 | | 匡明 | 男 | 95031 | | 王麗 | 女 | 95033 | | 李軍 | 男 | 95033 | | 王芳 | 女 | 95031 | | 陸軍 | 男 | 95031 | | 王尼瑪 | 男 | 95033 | | 張全蛋 | 男 | 95031 | | 趙鐵柱 | 男 | 95031 | +--------+------+-------+ 9 rows in set (0.00 sec)
-
查詢 teacher 表中不重複的 department 列
select distinct department from teacher; +------------+ | department | +------------+ | 計算機系 | | 電子工程系 | +------------+ 2 rows in set (0.02 sec)
-
查詢 score 表中成績在60-80之間的所有行
select degree from score where degree between 60 and 80; 或者 select degree from score where degree >60 and degree < 80; +--------+ | degree | +--------+ | 75 | | 79 | | 76 | | 68 | +--------+ 4 rows in set (0.02 sec)
-
查詢 score 表中成績爲 85, 86 或 88 的行
select * from score where degree in (85,86,88); +------+-------+--------+ | s_no | c_no | degree | +------+-------+--------+ | 103 | 3-245 | 86 | | 103 | 6-166 | 85 | | 105 | 3-105 | 88 | +------+-------+--------+
-
查詢 student 表中 ‘95031’ 班或性別爲 ‘女’ 的所有行
select * from student where class = 95031 or sex = '女'; +-----+--------+------+------------+-------+ | no | name | sex | birthday | class | +-----+--------+------+------------+-------+ | 102 | 匡明 | 男 | 1975-10-02 | 95031 | | 103 | 王麗 | 女 | 1976-01-23 | 95033 | | 105 | 王芳 | 女 | 1975-02-10 | 95031 | | 106 | 陸軍 | 男 | 1974-06-03 | 95031 | | 108 | 張全蛋 | 男 | 1975-02-10 | 95031 | | 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 | +-----+--------+------+------------+-------+ 6 rows in set (0.00 sec)
-
以 class 降序的方式查詢 student 表的所有行
select * from student order by class desc; -- DESC: 降序,從高到低 -- ASC(默認): 升序,從低到高 +-----+--------+------+------------+-------+ | no | name | sex | birthday | class | +-----+--------+------+------------+-------+ | 101 | 曾華 | 男 | 1977-09-01 | 95033 | | 103 | 王麗 | 女 | 1976-01-23 | 95033 | | 104 | 李軍 | 男 | 1976-02-20 | 95033 | | 107 | 王尼瑪 | 男 | 1976-02-20 | 95033 | | 102 | 匡明 | 男 | 1975-10-02 | 95031 | | 105 | 王芳 | 女 | 1975-02-10 | 95031 | | 106 | 陸軍 | 男 | 1974-06-03 | 95031 | | 108 | 張全蛋 | 男 | 1975-02-10 | 95031 | | 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 | +-----+--------+------+------------+-------+ 9 rows in set (0.00 sec)
- 以 c_no 升序、degree 降序查詢 score 表的所有行
select * from score order by c_no asc, degree desc; +------+-------+--------+ | s_no | c_no | degree | +------+-------+--------+ | 103 | 3-105 | 92 | | 105 | 3-105 | 88 | | 109 | 3-105 | 76 | | 103 | 3-245 | 86 | | 105 | 3-245 | 75 | | 109 | 3-245 | 68 | | 103 | 6-166 | 85 | | 109 | 6-166 | 81 | | 105 | 6-166 | 79 | +------+-------+--------+ 9 rows in set (0.00 sec)
-
查詢 “95031” 班的學生人數
select count(*) from student where class = 95031; +----------+ | count(*) | +----------+ | 5 | +----------+ 1 row in set (0.00 sec)
-
查詢 score 表中的最高分的學生學號和課程編號(子查詢或排序查詢)。
select max(degree) from score; //找到最高分 +-------------+ | max(degree) | +-------------+ | 92 | +-------------+ 1 row in set (0.00 sec) select * from score where degree = (select max(degree) from score);//找到最高分的編號 +------+-------+--------+ | s_no | c_no | degree | +------+-------+--------+ | 103 | 3-105 | 92 | +------+-------+--------+ 1 row in set (0.03 sec)
-
查詢每門課的平均成績
select avg(degree) from score group by c_no; +-------------+ | avg(degree) | +-------------+ | 85.3333 | | 76.3333 | | 81.6667 | +-------------+ 3 rows in set (0.02 sec)
-
查詢 score 表中至少有 2 名學生選修,並以 3 開頭的課程的平均分數
SELECT * FROM score; -- c_no 課程編號 +------+-------+--------+ | s_no | c_no | degree | +------+-------+--------+ | 103 | 3-105 | 92 | | 103 | 3-245 | 86 | | 103 | 6-166 | 85 | | 105 | 3-105 | 88 | | 105 | 3-245 | 75 | | 105 | 6-166 | 79 | | 109 | 3-105 | 76 | | 109 | 3-245 | 68 | | 109 | 6-166 | 81 | +------+-------+--------+ 分析表發現,至少有 2 名學生選修的課程是 3-105 、3-245 、6-166 ,以 3 開頭的課程是 3-105 、3-245 。也就是說,我們要查詢所有 3-105 和 3-245 的 degree 平均分。 -- 首先把 c_no, AVG(degree) 通過分組查詢出來 SELECT c_no, AVG(degree) FROM score GROUP BY c_no +-------+-------------+ | c_no | AVG(degree) | +-------+-------------+ | 3-105 | 85.3333 | | 3-245 | 76.3333 | | 6-166 | 81.6667 | +-------+-------------+ -- 再查詢出至少有 2 名學生選修的課程 -- HAVING: 表示持有 HAVING COUNT(c_no) >= 2 -- 並且是以 3 開頭的課程 -- LIKE 表示模糊查詢,"%" 是一個通配符,匹配 "3" 後面的任意字符。 AND c_no LIKE '3%'; -- 把前面的SQL語句拼接起來, -- 後面加上一個 COUNT(*),表示將每個分組的個數也查詢出來。 SELECT c_no, AVG(degree), COUNT(*) FROM score GROUP BY c_no HAVING COUNT(c_no) >= 2 AND c_no LIKE '3%'; +-------+-------------+----------+ | c_no | AVG(degree) | COUNT(*) | +-------+-------------+----------+ | 3-105 | 85.3333 | 3 | | 3-245 | 76.3333 | 3 | +-------+-------------+----------+
-
查詢所有學生的 name,以及該學生在 score 表中對應的 c_no 和 degree
SELECT no, name FROM student; +-----+-----------+ | no | name | +-----+-----------+ | 101 | 曾華 | | 102 | 匡明 | | 103 | 王麗 | | 104 | 李軍 | | 105 | 王芳 | | 106 | 陸軍 | | 107 | 王尼瑪 | | 108 | 張全蛋 | | 109 | 趙鐵柱 | +-----+-----------+ SELECT s_no, c_no, degree FROM score; +------+-------+--------+ | s_no | c_no | degree | +------+-------+--------+ | 103 | 3-105 | 92 | | 103 | 3-245 | 86 | | 103 | 6-166 | 85 | | 105 | 3-105 | 88 | | 105 | 3-245 | 75 | | 105 | 6-166 | 79 | | 109 | 3-105 | 76 | | 109 | 3-245 | 68 | | 109 | 6-166 | 81 | +------+-------+--------+
通過分析可以發現,只要把
score
表中的s_no
字段值替換成student
表中對應的name
字段值就可以了,如何做呢?
-
-- FROM...: 表示從 student, score 表中查詢
-- WHERE 的條件表示爲,只有在 student.no 和 score.s_no 相等時才顯示出來。
SELECT name, c_no, degree FROM student, score
WHERE student.no = score.s_no;
+-----------+-------+--------+
| name | c_no | degree |
+-----------+-------+--------+
| 王麗 | 3-105 | 92 |
| 王麗 | 3-245 | 86 |
| 王麗 | 6-166 | 85 |
| 王芳 | 3-105 | 88 |
| 王芳 | 3-245 | 75 |
| 王芳 | 6-166 | 79 |
| 趙鐵柱 | 3-105 | 76 |
| 趙鐵柱 | 3-245 | 68 |
| 趙鐵柱 | 6-166 | 81 |
+-----------+-------+--------+
.14.查詢所有學生的 no 、課程名稱 ( course 表中的 name ) 和成績 ( score 表中的 degree ) 列。
只有 score
關聯學生的 no
,因此只要查詢 score
表,就能找出所有和學生相關的 no
和 degree
:
SELECT s_no, c_no, degree FROM score;
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
| 103 | 3-245 | 86 |
| 103 | 6-166 | 85 |
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
然後查詢 course
表:
+-------+-----------------+
| no | name |
+-------+-----------------+
| 3-105 | 計算機導論 |
| 3-245 | 操作系統 |
| 6-166 | 數字電路 |
| 9-888 | 高等數學 |
+-------+-----------------+
只要把 score
表中的 c_no
替換成 course
表中對應的 name
字段值就可以了。
-- 增加一個查詢字段 name,分別從 score、course 這兩個表中查詢。
-- as 表示取一個該字段的別名。
SELECT s_no, name as c_name, degree FROM score, course
WHERE score.c_no = course.no;
+------+-----------------+--------+
| s_no | c_name | degree |
+------+-----------------+--------+
| 103 | 計算機導論 | 92 |
| 105 | 計算機導論 | 88 |
| 109 | 計算機導論 | 76 |
| 103 | 操作系統 | 86 |
| 105 | 操作系統 | 75 |
| 109 | 操作系統 | 68 |
| 103 | 數字電路 | 85 |
| 105 | 數字電路 | 79 |
| 109 | 數字電路 | 81 |
+------+-----------------+--------+
- 查詢所有學生的 name 、課程名 ( course 表中的 name ) 和 degree 。
只有 score
表中關聯學生的學號和課堂號,我們只要圍繞着 score
這張表查詢就好了。
SELECT * FROM score;
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
| 103 | 3-245 | 86 |
| 103 | 6-166 | 85 |
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
只要把 s_no
和 c_no
替換成 student
和 srouse
表中對應的 name
字段值就好了。
首先把 s_no
替換成 student
表中的 name
字段:
SELECT name, c_no, degree FROM student, score WHERE student.no = score.s_no;
+-----------+-------+--------+
| name | c_no | degree |
+-----------+-------+--------+
| 王麗 | 3-105 | 92 |
| 王麗 | 3-245 | 86 |
| 王麗 | 6-166 | 85 |
| 王芳 | 3-105 | 88 |
| 王芳 | 3-245 | 75 |
| 王芳 | 6-166 | 79 |
| 趙鐵柱 | 3-105 | 76 |
| 趙鐵柱 | 3-245 | 68 |
| 趙鐵柱 | 6-166 | 81 |
+-----------+-------+--------+
再把 c_no
替換成 course
表中的 name
字段:
-- 課程表
SELECT no, name FROM course;
+-------+-----------------+
| no | name |
+-------+-----------------+
| 3-105 | 計算機導論 |
| 3-245 | 操作系統 |
| 6-166 | 數字電路 |
| 9-888 | 高等數學 |
+-------+-----------------+
-- 由於字段名存在重複,使用 "表名.字段名 as 別名" 代替。
SELECT student.name as s_name, course.name as c_name, degree
FROM student, score, course
WHERE student.NO = score.s_no
AND score.c_no = course.no;
- 查詢 95031 班學生每門課程的平均成績。
在 score
表中根據 student
表的學生編號篩選出學生的課堂號和成績:
-- IN (..): 將篩選出的學生號當做 s_no 的條件查詢
SELECT s_no, c_no, degree FROM score
WHERE s_no IN (SELECT no FROM student WHERE class = '95031');
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
這時只要將 c_no
分組一下就能得出 95031
班學生每門課的平均成績:
SELECT c_no, AVG(degree) FROM score
WHERE s_no IN (SELECT no FROM student WHERE class = '95031')
GROUP BY c_no;
+-------+-------------+
| c_no | AVG(degree) |
+-------+-------------+
| 3-105 | 82.0000 |
| 3-245 | 71.5000 |
| 6-166 | 80.0000 |
+-------+-------------+
- 查詢在 3-105 課程中,所有成績高於 109 號同學的記錄。
首先篩選出課堂號爲 3-105
,在找出所有成績高於 109
號同學的的行。
SELECT * FROM score
WHERE c_no = '3-105'
AND degree > (SELECT degree FROM score WHERE s_no = '109' AND c_no = '3-105');
- 查詢所有成績高於 109 號同學的 3-105 課程成績記錄。
-- 不限制課程號,只要成績大於109號同學的3-105課程成績就可以。
SELECT * FROM score
WHERE degree > (SELECT degree FROM score WHERE s_no = '109' AND c_no = '3-105');
- 查詢所有和 101 、108 號學生同年出生的 no 、name 、birthday 列。
-- YEAR(..): 取出日期中的年份
SELECT no, name, birthday FROM student
WHERE YEAR(birthday) IN (SELECT YEAR(birthday) FROM student WHERE no IN (101, 108));
- 查詢 ‘張旭’ 教師任課的學生成績表。
首先找到教師編號:
SELECT NO FROM teacher WHERE NAME = '張旭'
通過 sourse
表找到該教師課程號:
SELECT NO FROM course WHERE t_no = ( SELECT NO FROM teacher WHERE NAME = '張旭' );
通過篩選出的課程號查詢成績表:
SELECT * FROM score WHERE c_no = (
SELECT no FROM course WHERE t_no = (
SELECT no FROM teacher WHERE NAME = '張旭'
)
);
- 查詢某選修課程多於5個同學的教師姓名。
首先在 teacher
表中,根據 no
字段來判斷該教師的同一門課程是否有至少5名學員選修:
-- 查詢 teacher 表
SELECT no, name FROM teacher;
+-----+--------+
| no | name |
+-----+--------+
| 804 | 李誠 |
| 825 | 王萍 |
| 831 | 劉冰 |
| 856 | 張旭 |
+-----+--------+
SELECT name FROM teacher WHERE no IN (
-- 在這裏找到對應的條件
);
查看和教師編號有有關的表的信息:
SELECT * FROM course;
-- t_no: 教師編號
+-------+-----------------+------+
| no | name | t_no |
+-------+-----------------+------+
| 3-105 | 計算機導論 | 825 |
| 3-245 | 操作系統 | 804 |
| 6-166 | 數字電路 | 856 |
| 9-888 | 高等數學 | 831 |
+-------+-----------------+------+
我們已經找到和教師編號有關的字段就在 course
表中,但是還無法知道哪門課程至少有5名學生選修,所以還需要根據 score
表來查詢:
-- 在此之前向 score 插入一些數據,以便豐富查詢條件。
INSERT INTO score VALUES ('101', '3-105', '90');
INSERT INTO score VALUES ('102', '3-105', '91');
INSERT INTO score VALUES ('104', '3-105', '89');
-- 查詢 score 表
SELECT * FROM score;
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 103 | 3-105 | 92 |
| 103 | 3-245 | 86 |
| 103 | 6-166 | 85 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
-- 在 score 表中將 c_no 作爲分組,並且限制 c_no 持有至少 5 條數據。
SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5;
+-------+
| c_no |
+-------+
| 3-105 |
+-------+
根據篩選出來的課程號,找出在某課程中,擁有至少5名學員的教師編號:
SELECT t_no FROM course WHERE no IN (
SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5
);
+------+
| t_no |
+------+
| 825 |
+------+
在 teacher
表中,根據篩選出來的教師編號找到教師姓名:
SELECT name FROM teacher WHERE no IN (
-- 最終條件
SELECT t_no FROM course WHERE no IN (
SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5
)
);
- 查詢 “計算機系” 課程的成績表。
思路是,先找出 course
表中所有 計算機系
課程的編號,然後根據這個編號查詢 score
表。
-- 通過 teacher 表查詢所有 `計算機系` 的教師編號
SELECT no, name, department FROM teacher WHERE department = '計算機系'
+-----+--------+--------------+
| no | name | department |
+-----+--------+--------------+
| 804 | 李誠 | 計算機系 |
| 825 | 王萍 | 計算機系 |
+-----+--------+--------------+
-- 通過 course 表查詢該教師的課程編號
SELECT no FROM course WHERE t_no IN (
SELECT no FROM teacher WHERE department = '計算機系'
);
+-------+
| no |
+-------+
| 3-245 |
| 3-105 |
+-------+
-- 根據篩選出來的課程號查詢成績表
SELECT * FROM score WHERE c_no IN (
SELECT no FROM course WHERE t_no IN (
SELECT no FROM teacher WHERE department = '計算機系'
)
);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-245 | 86 |
| 105 | 3-245 | 75 |
| 109 | 3-245 | 68 |
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 103 | 3-105 | 92 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 109 | 3-105 | 76 |
+------+-------+--------+
- 查詢 計算機系 與 電子工程系 中的不同職稱的教師。
-- NOT: 代表邏輯非
SELECT * FROM teacher WHERE department = '計算機系' AND profession NOT IN (
SELECT profession FROM teacher WHERE department = '電子工程系'
)
-- 合併兩個集
UNION
SELECT * FROM teacher WHERE department = '電子工程系' AND profession NOT IN (
SELECT profession FROM teacher WHERE department = '計算機系'
);
- 查詢課程 3-105 且成績 至少 高於 3-245 的 score 表。
SELECT * FROM score WHERE c_no = '3-105';
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 103 | 3-105 | 92 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 109 | 3-105 | 76 |
+------+-------+--------+
SELECT * FROM score WHERE c_no = '3-245';
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-245 | 86 |
| 105 | 3-245 | 75 |
| 109 | 3-245 | 68 |
+------+-------+--------+
-- ANY: 符合SQL語句中的任意條件。
-- 也就是說,在 3-105 成績中,只要有一個大於從 3-245 篩選出來的任意行就符合條件,
-- 最後根據降序查詢結果。
SELECT * FROM score WHERE c_no = '3-105' AND degree > ANY(
SELECT degree FROM score WHERE c_no = '3-245'
) ORDER BY degree DESC;
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
| 102 | 3-105 | 91 |
| 101 | 3-105 | 90 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 109 | 3-105 | 76 |
+------+-------+--------+
- 查詢課程 3-105 且成績高於 3-245 的 score 表。
-- 只需對上一道題稍作修改。
-- ALL: 符合SQL語句中的所有條件。
-- 也就是說,在 3-105 每一行成績中,都要大於從 3-245 篩選出來全部行纔算符合條件。
SELECT * FROM score WHERE c_no = '3-105' AND degree > ALL(
SELECT degree FROM score WHERE c_no = '3-245'
);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 103 | 3-105 | 92 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
+------+-------+--------+
- 查詢某課程成績比該課程平均成績低的 score 表。
-- 查詢平均分
SELECT c_no, AVG(degree) FROM score GROUP BY c_no;
+-------+-------------+
| c_no | AVG(degree) |
+-------+-------------+
| 3-105 | 87.6667 |
| 3-245 | 76.3333 |
| 6-166 | 81.6667 |
+-------+-------------+
-- 查詢 score 表
SELECT degree FROM score;
+--------+
| degree |
+--------+
| 90 |
| 91 |
| 92 |
| 86 |
| 85 |
| 89 |
| 88 |
| 75 |
| 79 |
| 76 |
| 68 |
| 81 |
+--------+
-- 將表 b 作用於表 a 中查詢數據
-- score a (b): 將表聲明爲 a (b),
-- 如此就能用 a.c_no = b.c_no 作爲條件執行查詢了。
SELECT * FROM score a WHERE degree < (
(SELECT AVG(degree) FROM score b WHERE a.c_no = b.c_no)
);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
- 查詢所有任課 ( 在 course 表裏有課程 ) 教師的 name 和 department。
SELECT name, department FROM teacher WHERE no IN (SELECT t_no FROM course);
+--------+-----------------+
| name | department |
+--------+-----------------+
| 李誠 | 計算機系 |
| 王萍 | 計算機系 |
| 劉冰 | 電子工程系 |
| 張旭 | 電子工程系 |
+--------+-----------------+
- 查詢 student 表中至少有 2 名男生的 class 。
-- 查看學生表信息
SELECT * FROM student;
+-----+-----------+-----+------------+-------+
| no | name | sex | birthday | class |
+-----+-----------+-----+------------+-------+
| 101 | 曾華 | 男 | 1977-09-01 | 95033 |
| 102 | 匡明 | 男 | 1975-10-02 | 95031 |
| 103 | 王麗 | 女 | 1976-01-23 | 95033 |
| 104 | 李軍 | 男 | 1976-02-20 | 95033 |
| 105 | 王芳 | 女 | 1975-02-10 | 95031 |
| 106 | 陸軍 | 男 | 1974-06-03 | 95031 |
| 107 | 王尼瑪 | 男 | 1976-02-20 | 95033 |
| 108 | 張全蛋 | 男 | 1975-02-10 | 95031 |
| 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 |
| 110 | 張飛 | 男 | 1974-06-03 | 95038 |
+-----+-----------+-----+------------+-------+
-- 只查詢性別爲男,然後按 class 分組,並限制 class 行大於 1。
SELECT class FROM student WHERE sex = '男' GROUP BY class HAVING COUNT(*) > 1;
+-------+
| class |
+-------+
| 95033 |
| 95031 |
+-------+
- 查詢 student 表中不姓 “王” 的同學記錄。
-- NOT: 取反
-- LIKE: 模糊查詢
mysql> SELECT * FROM student WHERE name NOT LIKE '王%';
+-----+-----------+-----+------------+-------+
| no | name | sex | birthday | class |
+-----+-----------+-----+------------+-------+
| 101 | 曾華 | 男 | 1977-09-01 | 95033 |
| 102 | 匡明 | 男 | 1975-10-02 | 95031 |
| 104 | 李軍 | 男 | 1976-02-20 | 95033 |
| 106 | 陸軍 | 男 | 1974-06-03 | 95031 |
| 108 | 張全蛋 | 男 | 1975-02-10 | 95031 |
| 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 |
| 110 | 張飛 | 男 | 1974-06-03 | 95038 |
+-----+-----------+-----+------------+-------+
- 查詢 student 表中每個學生的姓名和年齡。
-- 使用函數 YEAR(NOW()) 計算出當前年份,減去出生年份後得出年齡。
SELECT name, YEAR(NOW()) - YEAR(birthday) as age FROM student;
+-----------+------+
| name | age |
+-----------+------+
| 曾華 | 42 |
| 匡明 | 44 |
| 王麗 | 43 |
| 李軍 | 43 |
| 王芳 | 44 |
| 陸軍 | 45 |
| 王尼瑪 | 43 |
| 張全蛋 | 44 |
| 趙鐵柱 | 45 |
| 張飛 | 45 |
+-----------+------+
- 查詢 student 表中最大和最小的 birthday 值。
SELECT MAX(birthday), MIN(birthday) FROM student;
+---------------+---------------+
| MAX(birthday) | MIN(birthday) |
+---------------+---------------+
| 1977-09-01 | 1974-06-03 |
+---------------+---------------+
- 以 class 和 birthday 從大到小的順序查詢 student 表。
SELECT * FROM student ORDER BY class DESC, birthday;
+-----+-----------+-----+------------+-------+
| no | name | sex | birthday | class |
+-----+-----------+-----+------------+-------+
| 110 | 張飛 | 男 | 1974-06-03 | 95038 |
| 103 | 王麗 | 女 | 1976-01-23 | 95033 |
| 104 | 李軍 | 男 | 1976-02-20 | 95033 |
| 107 | 王尼瑪 | 男 | 1976-02-20 | 95033 |
| 101 | 曾華 | 男 | 1977-09-01 | 95033 |
| 106 | 陸軍 | 男 | 1974-06-03 | 95031 |
| 109 | 趙鐵柱 | 男 | 1974-06-03 | 95031 |
| 105 | 王芳 | 女 | 1975-02-10 | 95031 |
| 108 | 張全蛋 | 男 | 1975-02-10 | 95031 |
| 102 | 匡明 | 男 | 1975-10-02 | 95031 |
+-----+-----------+-----+------------+-------+
- 查詢 “男” 教師及其所上的課程。
SELECT * FROM course WHERE t_no in (SELECT no FROM teacher WHERE sex = '男');
+-------+--------------+------+
| no | name | t_no |
+-------+--------------+------+
| 3-245 | 操作系統 | 804 |
| 6-166 | 數字電路 | 856 |
+-------+--------------+------+
- 查詢最高分同學的 score 表。
-- 找出最高成績(該查詢只能有一個結果)
SELECT MAX(degree) FROM score;
-- 根據上面的條件篩選出所有最高成績表,
-- 該查詢可能有多個結果,假設 degree 值多次符合條件。
SELECT * FROM score WHERE degree = (SELECT MAX(degree) FROM score);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
+------+-------+--------+
- 查詢和 “李軍” 同性別的所有同學 name 。
-- 首先將李軍的性別作爲條件取出來
SELECT sex FROM student WHERE name = '李軍';
+-----+
| sex |
+-----+
| 男 |
+-----+
-- 根據性別查詢 name 和 sex
SELECT name, sex FROM student WHERE sex = (
SELECT sex FROM student WHERE name = '李軍'
);
+-----------+-----+
| name | sex |
+-----------+-----+
| 曾華 | 男 |
| 匡明 | 男 |
| 李軍 | 男 |
| 陸軍 | 男 |
| 王尼瑪 | 男 |
| 張全蛋 | 男 |
| 趙鐵柱 | 男 |
| 張飛 | 男 |
+-----------+-----+
- 查詢和 “李軍” 同性別且同班的同學 name 。
SELECT name, sex, class FROM student WHERE sex = (
SELECT sex FROM student WHERE name = '李軍'
) AND class = (
SELECT class FROM student WHERE name = '李軍'
);
+-----------+-----+-------+
| name | sex | class |
+-----------+-----+-------+
| 曾華 | 男 | 95033 |
| 李軍 | 男 | 95033 |
| 王尼瑪 | 男 | 95033 |
+-----------+-----+-------+
- 查詢所有選修 “計算機導論” 課程的 “男” 同學成績表。
需要的 “計算機導論” 和性別爲 “男” 的編號可以在 course
和 student
表中找到。
SELECT * FROM score WHERE c_no = (
SELECT no FROM course WHERE name = '計算機導論'
) AND s_no IN (
SELECT no FROM student WHERE sex = '男'
);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 104 | 3-105 | 89 |
| 109 | 3-105 | 76 |
+------+-------+--------+
- 建立一個
grade
表代表學生的成績等級,並插入數據:
CREATE TABLE grade (
low INT(3),
upp INT(3),
grade char(1)
);
INSERT INTO grade VALUES (90, 100, 'A');
INSERT INTO grade VALUES (80, 89, 'B');
INSERT INTO grade VALUES (70, 79, 'C');
INSERT INTO grade VALUES (60, 69, 'D');
INSERT INTO grade VALUES (0, 59, 'E');
SELECT * FROM grade;
+------+------+-------+
| low | upp | grade |
+------+------+-------+
| 90 | 100 | A |
| 80 | 89 | B |
| 70 | 79 | C |
| 60 | 69 | D |
| 0 | 59 | E |
+------+------+-------+
- 查詢所有學生的 s_no 、c_no 和 grade 列。
思路是,使用區間 ( BETWEEN
) 查詢,判斷學生的成績 ( degree
) 在 grade
表的 low
和 upp
之間。
SELECT s_no, c_no, grade FROM score, grade
WHERE degree BETWEEN low AND upp;
+------+-------+-------+
| s_no | c_no | grade |
+------+-------+-------+
| 101 | 3-105 | A |
| 102 | 3-105 | A |
| 103 | 3-105 | A |
| 103 | 3-245 | B |
| 103 | 6-166 | B |
| 104 | 3-105 | B |
| 105 | 3-105 | B |
| 105 | 3-245 | C |
| 105 | 6-166 | C |
| 109 | 3-105 | C |
| 109 | 3-245 | D |
| 109 | 6-166 | B |
+------+-------+-------+
tudent WHERE name = ‘李軍’;
±----+
| sex |
±----+
| 男 |
±----+
– 根據性別查詢 name 和 sex
SELECT name, sex FROM student WHERE sex = (
SELECT sex FROM student WHERE name = ‘李軍’
);
±----------±----+
| name | sex |
±----------±----+
| 曾華 | 男 |
| 匡明 | 男 |
| 李軍 | 男 |
| 陸軍 | 男 |
| 王尼瑪 | 男 |
| 張全蛋 | 男 |
| 趙鐵柱 | 男 |
| 張飛 | 男 |
±----------±----+
###
36. 查詢和 "李軍" 同性別且同班的同學 name 。
```mysql
SELECT name, sex, class FROM student WHERE sex = (
SELECT sex FROM student WHERE name = '李軍'
) AND class = (
SELECT class FROM student WHERE name = '李軍'
);
+-----------+-----+-------+
| name | sex | class |
+-----------+-----+-------+
| 曾華 | 男 | 95033 |
| 李軍 | 男 | 95033 |
| 王尼瑪 | 男 | 95033 |
+-----------+-----+-------+
- 查詢所有選修 “計算機導論” 課程的 “男” 同學成績表。
需要的 “計算機導論” 和性別爲 “男” 的編號可以在 course
和 student
表中找到。
SELECT * FROM score WHERE c_no = (
SELECT no FROM course WHERE name = '計算機導論'
) AND s_no IN (
SELECT no FROM student WHERE sex = '男'
);
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 104 | 3-105 | 89 |
| 109 | 3-105 | 76 |
+------+-------+--------+
- 建立一個
grade
表代表學生的成績等級,並插入數據:
CREATE TABLE grade (
low INT(3),
upp INT(3),
grade char(1)
);
INSERT INTO grade VALUES (90, 100, 'A');
INSERT INTO grade VALUES (80, 89, 'B');
INSERT INTO grade VALUES (70, 79, 'C');
INSERT INTO grade VALUES (60, 69, 'D');
INSERT INTO grade VALUES (0, 59, 'E');
SELECT * FROM grade;
+------+------+-------+
| low | upp | grade |
+------+------+-------+
| 90 | 100 | A |
| 80 | 89 | B |
| 70 | 79 | C |
| 60 | 69 | D |
| 0 | 59 | E |
+------+------+-------+
- 查詢所有學生的 s_no 、c_no 和 grade 列。
思路是,使用區間 ( BETWEEN
) 查詢,判斷學生的成績 ( degree
) 在 grade
表的 low
和 upp
之間。
SELECT s_no, c_no, grade FROM score, grade
WHERE degree BETWEEN low AND upp;
+------+-------+-------+
| s_no | c_no | grade |
+------+-------+-------+
| 101 | 3-105 | A |
| 102 | 3-105 | A |
| 103 | 3-105 | A |
| 103 | 3-245 | B |
| 103 | 6-166 | B |
| 104 | 3-105 | B |
| 105 | 3-105 | B |
| 105 | 3-245 | C |
| 105 | 6-166 | C |
| 109 | 3-105 | C |
| 109 | 3-245 | D |
| 109 | 6-166 | B |
+------+-------+-------+