mysql學習筆記(從安裝到入門練習)

mysql學習筆記

一、 安裝

官網:https://www.mysql.com/

下載:https://dev.mysql.com/downloads/mysql/

學習安裝(只爲學習,最好百度下載下面的集成工具,不要去官網下,太慢了)
(XAMMP,集成管理工具)免去數據庫安裝的漫長如果下載過慢,可以百度一下綠色軟件

XAMPP:https://www.apachefriends.org/index.html 注意:安裝路徑不要出現中文空格等字符

MySQL特點

  • 數據以表格的形式出現
    表格中每一行表示一組數據
    表格中每一列表示某組數據對應的字段(屬性)
    若干這樣的行和列就組成了一張表
  • 若干個表格組成一個庫

二、連接數據庫

在這裏插入圖片描述

在這裏插入圖片描述
注意:如果需要使用MySQL,需要首先啓動MySQL服務器

連接MySQL

  1. 通過 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 外鍵。如果設置了的話,personcardId 字段值爲 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 |
+----+------+-------+

總結

  1. 自動提交

    • 查看自動提交狀態:SELECT @@AUTOCOMMIT
    • 設置自動提交狀態:SET AUTOCOMMIT = 0
  2. 手動提交

    @@AUTOCOMMIT = 0 時,使用 COMMIT 命令提交事務。

  3. 事務回滾

    @@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、事務的隔離性

事務的隔離性可分爲四種 ( 性能從低到高 )

  1. READ UNCOMMITTED ( 讀取未提交 )

    如果有多個事務,那麼任意事務都可以看見其他事務的未提交數據

  2. READ COMMITTED ( 讀取已提交 )

    只能讀取到其他事務已經提交的數據

  3. REPEATABLE READ ( 可被重複讀 )

    如果有多個連接都開啓了事務,那麼事務之間不能共享數據記錄,否則只能共享已提交的記錄。

  4. 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;

習題練習

  1. 查詢 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)
    
    1. 查詢 teacher 表中不重複的 department 列

      select distinct department from teacher;
      +------------+
      | department |
      +------------+
      | 計算機系   |
      | 電子工程系 |
      +------------+
      2 rows in set (0.02 sec)
      
    2. 查詢 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)
    
    1. 查詢 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 |
      +------+-------+--------+
      
    2. 查詢 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)
      
    3. 以 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)
    
    1. 以 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)
    
    1. 查詢 “95031” 班的學生人數

      select count(*) from student where class = 95031;
      +----------+
      | count(*) |
      +----------+
      |        5 |
      +----------+
      1 row in set (0.00 sec)
      
      
    2. 查詢 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)
      
    3. 查詢每門課的平均成績

      select avg(degree) from score group by c_no;
      +-------------+
      | avg(degree) |
      +-------------+
      |     85.3333 |
      |     76.3333 |
      |     81.6667 |
      +-------------+
      3 rows in set (0.02 sec)
      
    4. 查詢 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 |
      +-------+-------------+----------+
      
    5. 查詢所有學生的 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 表,就能找出所有和學生相關的 nodegree

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 |
+------+-----------------+--------+

  1. 查詢所有學生的 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_noc_no 替換成 studentsrouse 表中對應的 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;

  1. 查詢 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 |
+-------+-------------+

  1. 查詢在 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');

  1. 查詢所有成績高於 109 號同學的 3-105 課程成績記錄。
-- 不限制課程號,只要成績大於109號同學的3-105課程成績就可以。
SELECT * FROM score
WHERE degree > (SELECT degree FROM score WHERE s_no = '109' AND c_no = '3-105');

  1. 查詢所有和 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));

  1. 查詢 ‘張旭’ 教師任課的學生成績表。

首先找到教師編號:

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 = '張旭' 
    )
);

  1. 查詢某選修課程多於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
    )
);

  1. 查詢 “計算機系” 課程的成績表。

思路是,先找出 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 |
+------+-------+--------+

  1. 查詢 計算機系 與 電子工程系 中的不同職稱的教師。
-- 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 = '計算機系'
);

  1. 查詢課程 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 |
+------+-------+--------+

  1. 查詢課程 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 |
+------+-------+--------+

  1. 查詢某課程成績比該課程平均成績低的 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 |
+------+-------+--------+

  1. 查詢所有任課 ( 在 course 表裏有課程 ) 教師的 name 和 department。
SELECT name, department FROM teacher WHERE no IN (SELECT t_no FROM course);
+--------+-----------------+
| name   | department      |
+--------+-----------------+
| 李誠   | 計算機系        |
| 王萍   | 計算機系        |
| 劉冰   | 電子工程系      |
| 張旭   | 電子工程系      |
+--------+-----------------+

  1. 查詢 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 |
+-------+

  1. 查詢 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 |
+-----+-----------+-----+------------+-------+

  1. 查詢 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 |
+-----------+------+

  1. 查詢 student 表中最大和最小的 birthday 值。
SELECT MAX(birthday), MIN(birthday) FROM student;
+---------------+---------------+
| MAX(birthday) | MIN(birthday) |
+---------------+---------------+
| 1977-09-01    | 1974-06-03    |
+---------------+---------------+

  1. 以 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 |
+-----+-----------+-----+------------+-------+

  1. 查詢 “男” 教師及其所上的課程。
SELECT * FROM course WHERE t_no in (SELECT no FROM teacher WHERE sex = '男');
+-------+--------------+------+
| no    | name         | t_no |
+-------+--------------+------+
| 3-245 | 操作系統     | 804  |
| 6-166 | 數字電路     | 856  |
+-------+--------------+------+

  1. 查詢最高分同學的 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 |
+------+-------+--------+

  1. 查詢和 “李軍” 同性別的所有同學 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 |
+-----------+-----+
| 曾華      | 男  |
| 匡明      | 男  |
| 李軍      | 男  |
| 陸軍      | 男  |
| 王尼瑪    | 男  |
| 張全蛋    | 男  |
| 趙鐵柱    | 男  |
| 張飛      | 男  |
+-----------+-----+

  1. 查詢和 “李軍” 同性別且同班的同學 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 |
+-----------+-----+-------+

  1. 查詢所有選修 “計算機導論” 課程的 “男” 同學成績表。

需要的 “計算機導論” 和性別爲 “男” 的編號可以在 coursestudent 表中找到。

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 |
+------+-------+--------+

  1. 建立一個 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     |
+------+------+-------+

  1. 查詢所有學生的 s_no 、c_no 和 grade 列。

思路是,使用區間 ( BETWEEN ) 查詢,判斷學生的成績 ( degree ) 在 grade 表的 lowupp 之間。

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 |
+-----------+-----+-------+

  1. 查詢所有選修 “計算機導論” 課程的 “男” 同學成績表。

需要的 “計算機導論” 和性別爲 “男” 的編號可以在 coursestudent 表中找到。

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 |
+------+-------+--------+

  1. 建立一個 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     |
+------+------+-------+

  1. 查詢所有學生的 s_no 、c_no 和 grade 列。

思路是,使用區間 ( BETWEEN ) 查詢,判斷學生的成績 ( degree ) 在 grade 表的 lowupp 之間。

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     |
+------+-------+-------+

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章