炸裂!MySQL 82 張圖帶你飛
阿新 • • 發佈:2021-01-26
之前兩篇文章帶你瞭解了 MySQL 的基礎語法和 MySQL 的進階內容,那麼這篇文章我們來了解一下 MySQL 中的高階內容。
其他文章:
[138 張圖帶你 MySQL 入門](https://mp.weixin.qq.com/s?__biz=MzkwMDE1MzkwNQ==&mid=2247495908&idx=1&sn=e6e4d2fdb48ee5cfaea7ff2c200b0800&chksm=c04ae7baf73d6eacee0ffbafb16cc9851d2ce2906c59aa06f619b6254e3e8074af7f3b5c6b3a&scene=21#wechat_redirect)
[47 張圖帶你 MySQL 進階!!!](https://mp.weixin.qq.com/s?__biz=MzkwMDE1MzkwNQ==&mid=2247495882&idx=1&sn=488a147b3bf3c710806ce7031e6186b5&chksm=c04ae794f73d6e829f59b76657db17c50daab3633cc488629efbf05b675209a23ff96bab9f90&scene=21#wechat_redirect)
[炸裂!MySQL 82 張圖帶你飛!](https://mp.weixin.qq.com/s?__biz=MzkwMDE1MzkwNQ==&mid=2247496596&idx=1&sn=2c317201d150511e3036a646e853c169&chksm=c04ae4caf73d6ddce509eb8eb7aaa014aadc0910fff20aa6859112b8fb7215151216f2eb89c2&token=1139647206&lang=zh_CN#rd)
本文思維導圖如下。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126091153998-40966910.png)
## 事務控制和鎖定語句
我們知道,MyISAM 和 MEMORY 儲存引擎支援`表級鎖定(table-level locking)`,InnoDB 儲存引擎支援`行級鎖定(row-level locking)`,BDB 儲存引擎支援`頁級鎖定(page-level locking)`。各個鎖定級別的特點如下
頁級鎖:銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般
表級鎖:表級鎖是對整張表進行加鎖,MyISAM 和 MEMORY 主要支援表級鎖,表級鎖加鎖快,不會出現死鎖,鎖的粒度比較粗,併發度最低
行級鎖:行級鎖可以說是 MySQL 中粒度最細的一種鎖了,InnoDB 支援行級鎖,行級鎖容易發生死鎖,併發度比較好,同時鎖的開銷也比較大。
MySQL 預設情況下支援表級鎖定和行級鎖定。但是在某些情況下需要手動控制事務以確保整個事務的完整性,下面我們就來探討一下事務控制。但是在探討事務控制之前我們先來認識一下兩個鎖定語句
### 鎖定語句
MySQL 的鎖定語句主要有兩個 `Lock` 和 `unLock`,Lock Tables 可用於鎖定當前執行緒的表,就跟 Java 語法中的 Lock 鎖的用法是一樣的,如果表鎖定,意味著其他執行緒不能再操作表,直到鎖定被釋放為止。如下圖所示
```mysql
lock table cxuan005 read;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090302606-1943133758.png)
我們鎖定了 cxuan005 的 read 鎖,然後這時我們再進行一次查詢,看看是否能夠執行這條語句
```mysql
select * from cxuan005 where id = 111;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090309674-508085611.png)
可以看到,在進行 read 鎖定了,我們仍舊能夠執行查詢語句。
現在我們另外起一個視窗,相當於另起了一個執行緒來進行查詢操作。
```mysql
select * from cxuan005;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090317483-964008152.png)
這是第二個視窗執行查詢的結果,可以看到,在一個執行緒執行 read 鎖定後,其他執行緒仍然可以進行表的查詢操作。
那麼第二個執行緒能否執行更新操作呢?我們來看一下
```mysql
update cxuan005 set info='cxuan' where id = 111;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090324892-573993033.png)
發生了什麼?怎麼沒有提示結果呢?其實這個情況下表示 cxuan005 已經被加上了 read 鎖,由於當前執行緒不是持有鎖的執行緒,所以當前執行緒無法執行更新。
### 解鎖語句
現在我們把視窗切換成持有 read 鎖的執行緒,來進行 read 鎖的解鎖
```mysql
unlock tables;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090333798-139920609.png)
在解鎖完成前,進行更新的執行緒會一直等待,直到解鎖完成後,才會進行更新。我們可以看一下更新執行緒的結果。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090341962-408246521.png)
可以看到,執行緒已經更新完畢,我們看一下更新的結果
```mysql
select * from cxuan005 where id = 111;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090348859-900939123.png)
如上圖所示,id = 111 的值已經被更新成了 cxuan。
## 事務控制
`事務(Transaction)` 是訪問和更新資料庫的基本執行單元,一個事務中可能會包含多個 SQL 語句,事務中的這些 SQL 語句要麼都執行,要麼都不執行,而 MySQL 它是一個關係型資料庫,它自然也是支援事務的。事務同時也是區分關係型資料庫和非關係型資料庫的一個重要的方面。
在 MySQL 事務中,主要涉及的語法包含 **SET AUTOCOMMIT、START TRANSACTION、COMMIT 和 ROLLBACK** 等。
### 自動提交
在 MySQL 中,事務預設是`自動提交(Autocommit)`的,如下所示
```mysql
show variables like 'autocommit';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090356024-159223612.png)
在自動提交的模式下,每個 SQL 語句都會當作一個事務執行提交操作,例如我們上面使用的更新語句
```mysql
update cxuan005 set info='cxuan' where id = 111;
```
> 如果想要關閉資料庫的自動提交應該怎麼做呢?
其實,MySQL 是可以關閉自動提交的,你可以執行
```mysql
set autocommit = 0;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090409366-822486088.png)
然後我們再看一下自動提交是否關閉了,再次執行一下 show variables like 'autocommit' 語句
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090422814-1757332269.png)
可以看到,自動提交已經關閉了,再次執行
```mysql
set autocommit = 1;
```
會再次開啟自動提交。
>這裡注意一下特殊操作。
>
>在 MySQL 中,存在一些特殊的命令,如果在事務中執行了這些命令,會馬上強制執行 commit 提交事務;比如 DDL 語句(create table/drop table/alter/table)、lock tables 語句等等。
>
>不過,常用的 select、insert、update 和 delete命令,都不會強制提交事務。
### 手動提交
如果需要手動 commit 和 rollback 的話,就需要明確的事務控制語句了。
典型的 MySQL 事務操作如下
```mysql
start transaction;
... # 一條或者多條語句
commit;
```
上面程式碼中的 start transaction 就是事務的開始語句,編寫 SQL 後會呼叫 commit 提交事務,然後將事務統一執行,如果 SQL 語句出現錯誤會自動呼叫 Rollback 進行回滾。
下面我們就通過示例來演示一下 MySQL 的事務,同樣的,我們需要啟動兩個視窗來演示,為了便於區分,我們使用 mysql01 和 mysql02 來命名。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090432928-1628467467.png)
我們用 `start transaction` 命令啟動一個事務,然後再 cxuan005 表中插入一條資料,此時 mysql02 不做任何操作。涉及的 SQL 語句如下。
```mysql
start transaction;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090439943-1664054681.png)
然後執行
```mysql
select * from cxuan005;
```
查詢一下 cxuan005 中的資料
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090447383-1104049953.png)
嗯。。。很多長度太長了,現在我們把所有的 info 資料都更新為 cxuan 。
```mysql
update cxuan005 set info='cxuan';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090454459-1625155031.png)
更新完畢後,我們先不提交事務,分別在 mysql01 和 mysql02 中進行查詢,發現只有 mysql01 視窗中的查詢已經生效,而 mysql02 中還是更新前的資料
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090501288-1337995880.png)
現在我們在 mysql01 中 commit 當前事務,然後在 mysql02 中查詢,發現數據已經被修改了。
除了 commit 之外,MySQL 中還有 `commit and chain` 命令,這個命令會提交當前事務並且重新開啟一個新的事務。如下程式碼所示
```mysql
start transaction; # 開啟一個新的事務
insert into cxuan005(id,info) values (555,'cxuan005'); # 插入一條資料
commit and chain; # 提交當前事務並重新開啟一個事務
```
上面是一個事務操作,在 commit and chain 鍵入後,我們可以再次執行 SQL 語句
```mysql
update cxuan005 set info = 'cxuan' where id = 555;
commit;
```
然後再次查詢
```mysql
select * from cxuan005;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090511061-90924217.png)
執行後,可以發現,我們僅僅使用了一個 start transaction 命令就執行了兩次事務操作。
如果在手動提交的事務中,你發現有一條 SQL 語句寫的不正確或者有其他原因需要回滾,那麼此時你就會用到 `rollback` 語句,它會回滾當前事務,相當於什麼也沒發生。如下程式碼所示。
```mysql
start transaction;
delete from cxuan005 where id = 555;
rollback;
```
>這裡`切忌`一點:delete 刪除語句一定要加 where ,不加 where 語句的刪除就是耍流氓。
在同一個事務操作中,最好使用相同儲存引擎的表,如果使用不同儲存引擎的表後,rollback 語句會對非事務型別的表進行特別處理,因此 commit 、rollback 只能對事務型別的表進行提交和回滾。
我們提交的事務一般都會被記錄到二進位制的日誌中,但是如果一個事務中包含非事務型別的表,那麼回滾操作也會被記錄到二進位制日誌中,以確保非事務型別的表可以被複制到從資料庫中。
這裡解釋一下什麼是事務表和非事務表
#### 事務表和非事務表
事務表故名思義就是支援事務的表,支不支援事務和 MySQL 的儲存型別有關,一般情況下,`InnoDB` 儲存引擎的表是支援事務的,關於 InnoDB 的知識,我們會在後面詳細介紹。
非事務表相應的就是不支援事務的表,在 MySQL 中,儲存引擎 `MyISAM` 是不支援事務的,非事務表的特點是不支援回滾。
對於回滾的話,還要講一點就是 `SAVEPOINT`,它能指定事務回滾的一部分,但是不能指定事務提交的一部分。 SAVEPOINT 可以指定多個,在滿足不同條件的同時,回滾不同的 SAVEPOINT。需要注意的是,如果定義了兩個相同名稱的 SAVEPOINT,則後面定義的 SAVEPOINT 會覆蓋之前的定義。如果 SAVEPOINT 不再需要的話,可以通過 `RELEASE SAVEPOINT` 來進行刪除。刪除後的 SAVEPOINT 不能再執行 ROLLBACK TO SAVEPOINT 命令。
我們通過一個示例來進行模擬不同的 SAVEPOINT
首先先啟動一個事務 ,向 cxuan005 中插入一條資料,然後進行查詢,那麼是可以查詢到這條記錄的
```mysql
start transaction;
insert into cxuan005(id,info) values(666,'cxuan666');
select * from cxuan005 where id = 666;
```
查詢之後的記錄如下
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090521178-755557912.png)
然後我們定義一個 SAVEPOINT,如下所示
```mysql
savepoint test;
```
然後繼續插入一條記錄
```mysql
insert into cxuan005(id,info) values(777,'cxuan777');
```
此時就可以查詢到兩條新增記錄了,id 是 666 和 777 的記錄。
```mysql
select * from cxuan005 where id = 777;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090535724-931446452.png)
那麼我們可以回滾到剛剛定義的 SAVEPOINT
```mysql
rollback to savepoint test;
```
再次查詢 cxuan005 這個表,可以看到,只有 id=666 的這條記錄插入進來了,說明 id=777 這條記錄已經被回滾了。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090543658-1361224578.png)
此時我們看到的都是 mysql01 中事務還沒有提交前的狀態,所以這時候 mysql02 中執行查詢操作是看不到 666 這條記錄的。
然後我們在 mysql01 中執行 commit 操作,那麼此時在 mysql02 中就可以查詢到這條記錄了。
## SQL 安全問題
SQL 安全問題應該是我們程式設計師比較忽視的一個地方了。日常開發中,我們一般只會關心 SQL 能不能解決我們的業務問題,能不能把資料查出來,而對於 SQL 問題,我們一般都認為這是 DBA 的活,其實我們 CRUD 程式設計師也應該瞭解一下 SQL 的安全問題。
### SQL 注入簡介
SQL 注入就是利用某些資料庫的外部介面將使用者資料插入到實際的 SQL 中,從而達到入侵`資料庫`的目的。SQL 注入是一種常見的網路攻擊的方式,它不是利用作業系統的 BUG 來實現攻擊的。SQL 主要是針對程式設計師編寫時的疏忽來入侵的。
SQL 注入攻擊有很大的危害,攻擊者可以利用它讀取、修改或者刪除資料庫內的資料,獲取資料庫中的使用者名稱和密碼,甚至獲得資料庫管理員的許可權。並且 SQL 注入一般比較難以防範。
## SQL Mode
MySQL 可以執行在不同的 SQL Mode 模式下,不同的 SQL Mode 定義了不同的 SQL 語法,資料校驗規則,這樣就能夠在不同的環境中使用 MySQL ,下面我們就來介紹一下 SQL Mode。
### SQL Mode 解決問題
SQL Mode 可以解決下面這幾種問題
* 通過設定 SQL Mode,可以完成不同嚴格程度的資料校驗,有效保障資料的準確性。
* 設定 SQL Mode 為 `ANSI` 模式,來保證大多數 SQL 符合標準的 SQL 語法,這樣應用在不同資料庫的遷移中,不需要對 SQL 進行較大的改變
* 資料在不同資料庫的遷移中,通過改變 SQL Mode 能夠更方便的進行遷移。
下面我們就通過示例來演示一下 SQL Mode 用法
我們可以通過
```mysql
select @@sql_mode;
```
來檢視預設的 SQL Mode,如下是我的資料庫所支援的 SQL Mode
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090553910-1792731169.png)
涉及到很多 SQL Mode,下面是這些 SQL Mode 的解釋
`ONLY_FULL_GROUP_BY`:這個模式會對 GROUP BY 進行合法性檢查,對於 GROUP BY 操作,如果在SELECT 中的列,沒有在 GROUP BY 中出現,那麼將認為這個 SQL 是不合法的,因為列不在 GROUP BY 從句中
同樣舉個例子,我們現在查詢一下 cxuan005 的 id 和 info 欄位。
```mysql
select id,info from cxuan005;
```
這樣是可以執行的
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090600909-1998834953.png)
然後我們使用 GROUP BY 字句進行分組,這裡只對 info 進行分組,我們看一下會出現什麼情況
```mysql
select id,info from cxuan005 group by info;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090614492-206646599.png)
我們可以從錯誤原因中看到,這條 SQL 語句是不符合 ONLY_FULL_GROUP_BY 的這條 SQL Mode 的。因為我們只對 info 進行分組了,沒有對 id 進行分組,我們把 SQL 語句改成如下形式
```mysql
select id,info from cxuan005 group by id,info;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090620368-128663274.png)
這樣 SQL 就能正確執行了。
當然,我們也可以刪除 sql_mode = ONLY_FULL_GROUP_BY 的這條 Mode,可以使用
```mysql
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
```
來進行刪除,刪除後我們使用分組語句就可以放飛自我了。
```mysql
select id,info from cxuan005 group by info;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090628940-545914733.png)
但是這種做法只是暫時的修改,我們可以修改配置檔案 my.ini 中的 sql_mode= STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
`STRICT_TRANS_TABLES`:這就是嚴格模式,在這個模式下會對資料進行嚴格的校驗,錯誤資料不能插入,報error 錯誤。如果不能將給定的值插入到事務表中,則放棄該語句。對於非事務表,如果值出現在單行語句或多行語句的第1行,則放棄該語句。
>當使用 innodb 儲存引擎表時,考慮使用 innodb_strict_mode 模式的 sql_mode,它能增量額外的錯誤檢測功能。
`NO_ZERO_IN_DATE`:這個模式影響著日期中的月份和天數是否可以為 0(注意年份是非 0 的),這個模式也取決於嚴格模式是否被啟用。如果這個模式未啟用,那麼日期中的零部分被允許並且插入沒有警告。如果這個模式啟用,那麼日期中的零部分插入被作為 `0000-00-00` 並且產生一個警告。
這個模式需要注意下,如果啟用的話,需要 `STRICT_TRANS_TABLES` 和 `NO_ZERO_IN_DATE` 同時啟用,否則不起作用,也就是
```mysql
set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE';
```
然後我們換表了,使用 cxuan003 這張表,表結構如下
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090637950-506219596.png)
我們主要測試日期的使用,在 cxuan003 中插入一條日期為 `0000-00-00` 的資料
```mysql
insert into cxuan003 values(111,'study','0000-00-00');
```
發現能夠執行成功,但是把年月日各自變為 0 之後再進行插入,則會插入失敗。
```mysql
insert into cxuan003 values(111,'study','2021-00-00');
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090646748-1935130935.png)
```mysql
insert into cxuan003 values(111,'study','2021-01-00');
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090654588-678992341.png)
這些組合有很多,我這裡就不再細緻演示了,讀者可以自行測試。
如果要插入 `0000-00-00` 這樣的資料,必須設定 `NO_ZERO_IN_DATE` 和 `NO_ZERO_DATE`。
`ERROR_FOR_DIVISION_BY_ZERO`:如果這個模式未啟用,那麼零除操作將會插入空值並且不會產生警告;如果這個模式啟用,零除操作插入空值併產生警告;如果這個模式和嚴格模式都啟用,零除從操作將會產生一個錯誤。
`NO_AUTO_CREATE_USER`:禁止使用 grant 語句自動建立使用者,除非認證資訊被指定。
`NO_ENGINE_SUBSTITUTION`:此模式指定當執行 create 語句或者 alter 語句指定的儲存引擎沒有啟用或者沒有編譯時,控制預設儲存引擎的自動切換。預設是啟用狀態的。
### SQL Mode 三種作用域
SQL Mode 按作用區域和時間可分為 3。個級別,分別是**會話級別,全域性級別,配置(永久生效)級別**。
我們上面使用的 SQL Mode 都是 `會話級別`,會話級別就是當前視窗域有效。它的設定方式是
```mysql
set @@session.sql_mode='xx_mode'
set session sql_mode='xx_mode'
```
全域性域就是當前會話關閉不失效,但是在 MySQL 重啟後失效。它的設定方式是
```mysql
set global sql_mode='xx_mode';
set @@global.sql_mode='xx_mode';
```
配置域就是在 `vi /etc/my.cnf` 裡面新增
```mysql
[mysqld]
sql-mode = "xx_mode"
```
配置域在儲存退出後,重啟伺服器,即可永久生效。
## SQL 正則表示式
正則表示式相信大家應該都用過,不過你在 MySQL 中用過正則表示式嗎?下面我們就來聊一聊 SQL 中的正則表示式。
`正則表示式(Regular Expression)` 是指一個用來描述或者匹配字串的句法規則。正則表示式通常用來檢索和替換某個文字中的文字內容。很多語言都支援正則表示式,MySQL 同樣也不例外,MySQL 利用 `REGEXP` 命令提供給使用者擴充套件的正則表示式功能。下面是 MySQL 中正則表示式的一些規則。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090710711-1179189757.png)
下面來演示一下正則表示式的用法
* `^` 在字串的開始進行匹配,根據返回的結果來判斷是否匹配,1 = 匹配,0 = 不匹配。下面嘗試匹配字串 `aaaabbbccc` 是否以字串 `a` 為開始
```mysql
select 'aaaabbbccc' regexp '^a';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090720505-1626663578.png)
* 同樣的,`$` 會在末尾處進行匹配,如下所示
```mysql
select 'aaaabbbccc' regexp 'c$';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090731633-71886249.png)
* `.` 匹配單個任意字元
```mysql
select 'berska' regexp '.s', 'zara' regexp '.a';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090738827-593173661.png)
* `[...]` 表示匹配括號內的任意字元,示例如下
```mysql
select 'whosyourdaddy' regexp '[abc]';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090746117-1206704193.png)
* `[^...]` 匹配括號內不包含的任意字元,和 `[...]` 是相反的,如果有任何匹配不上,返回 0 ,全部匹配上返回 1。
```mysql
select 'x' regexp '[^xyz]';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090752548-1426663668.png)
* `n*` 表示匹配零個或者多個 n 字串,如下
```mysql
select 'aabbcc' regexp 'd*';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090759969-1760968634.png)
沒有 d 出現也可以返回 1 ,因為 * 表示 0 或者多個。
* `n+` 表示匹配 1 個或者 n 個字串
```mysql
select 'aabbcc' regexp 'd+';
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090806205-421261387.png)
* `n?` 的用法和 n+ 類似,只不過 n? 可以匹配空串
## 常見 SQL 技巧
### RAND() 函式
大多數資料庫都會提供產生隨機數的函式,通過這些函式可以產生隨機數,也可以使用從資料庫表中抽取隨機產生的記錄,這對統計分析來說很有用。
在 MySQL 中,通常使用 `RAND()` 函式來產生隨機數。RAND() 和 ORDER BY 組合完成資料抽取功能,如下所示。
我們新建一張表用於資料檢索。
```mysql
CREATE TABLE `clerk_Info` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`salary` decimal(10,2) DEFAULT NULL,
`companyId` int(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
```
然後插入一些資料,插入完成後的資料如下。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090816183-1010434690.png)
然後我們可以使用 RAND() 函式進行隨機檢索資料行
```mysql
select * from clerk_info order by rand();
```
檢索完成後的資料如下
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090823587-1089516893.png)
多次查詢後發現每次檢索的資料順序都是隨機的。
這個函式多用於隨機抽樣,比如選取一定數量的樣本在進行隨機排序,需要用到 `limit` 關鍵字。
### GROUP BY + WITH ROLLUP
我們經常使用 GROUP BY 語句,但是你用過 `GROUP BY` 和 `WITH ROLLUP` 一起使用的嗎?使用 GROUP BY 和 WITH ROLLUP 字句可以檢索出更多的分組集合資訊。
我們仍舊對 clerk_info 表進行操作,我們對 name 和 salary 進行分組統計工資總數。
```mysql
select name,sum(salary) from clerk_info group by name with rollup;
```
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090831555-400417053.png)
可以看到上面的表按照 name 進行分組,然後再對 money 進行統計。
也就是說 GROUP BY 語句執行完成後可以滿足使用者想要的任何一個分組以及分組組合的聚合資訊值。
>這裡需要注意一點,不能同時使用 ORDER BY 字句對結果進行排序,ROLLUP 和 ORDER BY 是互斥的。
### 資料庫名、表名大小寫問題
在 MySQL 中,**資料庫中的每個表至少對應資料庫目錄中的一個檔案,當然這取決於儲存引擎的實現了**。不同的作業系統對大小寫的敏感性決定了資料庫和表名的大小寫的敏感性。在 UNIX 作業系統中是對大小寫敏感的,因此資料庫名和表名也是具有敏感性的,而到了 Windows 則不存在敏感性問題,因為 Windows 作業系統本身對大小寫不敏感。**列、索引、觸發器**在任何平臺上都對大小寫不敏感。
在 MySQL 中,資料庫名和表名是由 `lower_case_tables_name` 系統變數決定的。可以在啟動 `mysqld` 時設定這個系統變數。下面是 `lower_case_tables_name` 的值。
![](https://img2020.cnblogs.com/blog/1515111/202101/1515111-20210126090840052-1335289634.png)
如果只在一個平臺上使用 MySQL 的話,通常不需要修改 `lower_case_tables_name` 變數。如果想要在不同系統系統之間遷移表就會涉及到大小寫問題,因為 UNIX 中 clerk_info 和 CLERK_INFO 被認為是兩個不同的表,而 Windows 中則認為是一個。在 UNIX 中使用 lower_case_tables_name=0, 而在 Windows 中使用lower_case_tables_name=2,這樣可以保留資料庫名和表名的大小寫,但是不能保證所有的 SQL 查詢中使用的表名和資料庫名的大小寫相同。如果 SQL 語句中沒有正確引用資料庫名和表名的大小寫,那麼雖然在 Windows 中能正確執行,但是如果將查詢轉移到 UNIX 中,大小寫不正確,將會導致查詢失敗。
### 外來鍵問題
這裡需要注意一個問題,`InnoDB` 儲存引擎是支援外來鍵的,而 `MyISAM` 儲存引擎是不支援外來鍵的,因此在 MyISAM 中設定外來鍵會不起作用。
## MySQL 常用函式
下面我們來了解一下 MySQL 函式,MySQL 函式也是我們日常開發過程中經常使用的,選用合適的函式能夠提高我們的開發效率,下面我們就來一起認識一下這些函式
### 字串函式
字串函式是最常用的一種函數了,MySQL 也是支援很多種字串函式,下面是 MySQL 支援的字串函式表
| 函式 | 功能 |
| --------- | -------------------------- |
| LOWER | 將字串所有字元變為小寫 |
| UPPER | 將字串所有字元變為大寫 |
| CONCAT | 進行字串拼接 |
| LEFT | 返回字串最左邊的字元 |
| RIGHT | 返回字串最右邊的字元 |
| INSERT | 字串替換 |
| LTRIM | 去掉字串左邊的空格 |
| RTRIM | 去掉字串右邊的空格 |
| REPEAT | 返回重複的結果 |
| TRIM | 去掉字串行尾和行頭的空格 |
| SUBSTRING | 返回指定的字串 |
| LPAD | 用字串對最左邊進行填充 |
| RPAD | 用字串對最右邊進行填充 |
| STRCMP | 比較字串 s1 和 s2 |
| REPLACE | 進行字串替換 |
下面通過具體的示例演示一下每個函式的用法
* LOWER(str) 和 UPPER(str) 函式:用於轉換大小寫
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191023868-1180414321.png)
* CONCAT(s1,s2 ... sn) :把傳入的引數拼接成一個字串
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191029939-1245733967.png)
上面把 `c xu an` 拼接成為了一個字串,另外需要注意一點,任何和 NULL 進行字串拼接的結果都是 NULL。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191036527-900368959.png)
* LEFT(str,x) 和 RIGHT(str,x) 函式:分別返回字串最左邊的 x 個字元和最右邊的 x 個字元。如果第二個引數是 NULL,那麼將不會返回任何字串
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191048132-2112982563.png)
* INSERT(str,x,y,instr) : 將字串 str 從指定 x 的位置開始, 取 y 個長度的字串替換為 instr。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191055230-407223724.png)
* LTRIM(str) 和 RTRIM(str) 分別表示去掉字串 str 左側和右側的空格
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191103526-1822256316.png)
* REPEAT(str,x) 函式:返回 str 重複 x 次的結果
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191111685-440396397.png)
* TRIM(str) 函式:用於去掉目標字串的空格
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191119119-1227375535.png)
* SUBSTRING(str,x,y) 函式:返回從字串 str 中第 x 位置起 y 個字元長度的字串
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191125284-1785064004.png)
* LPAD(str,n,pad) 和 RPAD(str,n,pad) 函式:用字串 pad 對 str 左邊和右邊進行填充,直到長度為 n 個字元長度
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191131796-1920054468.png)
* STRCMP(s1,s2) 用於比較字串 s1 和 s2 的 ASCII 值大小。如果 s1 < s2,則返回 -1;如果 s1 = s2 ,返回 0 ;如果 s1 > s2 ,返回 1。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191139600-1982730833.png)
* REPLACE(str,a,b) : 用字串 b 替換字串 str 種所有出現的字串 a
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191146695-991154817.png)
### 數值函式
MySQL 支援數值函式,這些函式能夠處理很多數值運算。下面我們一起來學習一下 MySQL 中的數值函式,下面是所有的數值函式
| 函式 | 功能 |
| -------- | -------------------------- |
| ABS | 返回絕對值 |
| CEIL | 返回大於某個值的最大整數值 |
| MOD | 返回模 |
| ROUND | 四捨五入 |
| FLOOR | 返回小於某個值的最大整數值 |
| TRUNCATE | 返回數字截斷小數的結果 |
| RAND | 返回 0 - 1 的隨機值 |
下面我們還是以實踐為主來聊一聊這些用法
* ABS(x) 函式:返回 x 的絕對值
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191159438-1901367850.png)
* CEIL(x) 函式: 返回大於 x 的整數
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191206305-1318889217.png)
* MOD(x,y),對 x 和 y 進行取模操作
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191213840-1660963808.png)
* ROUND(x,y) 返回 x 四捨五入後保留 y 位小數的值;如果是整數,那麼 y 位就是 0 ;如果不指定 y ,那麼 y 預設也是 0 。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191223700-871496702.png)
* FLOOR(x) : 返回小於 x 的最大整數,用法與 CEIL 相反
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191230311-1725783253.png)
* TRUNCATE(x,y): 返回數字 x 截斷為 y 位小數的結果, TRUNCATE 知識截斷,並不是四捨五入。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191237831-1256203391.png)
* RAND() :返回 0 到 1 的隨機值
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191244983-1131057062.png)
### 日期和時間函式
日期和時間函式也是 MySQL 中非常重要的一部分,下面我們就來一起認識一下這些函式
| 函式 | 功能 |
| -------------- | -------------------------------- |
| NOW | 返回當前的日期和時間 |
| WEEK | 返回一年中的第幾周 |
| YEAR | 返回日期的年份 |
| HOUR | 返回小時值 |
| MINUTE | 返回分鐘值 |
| MONTHNAME | 返回月份名 |
| CURDATE | 返回當前日期 |
| CURTIME | 返回當前時間 |
| UNIX_TIMESTAMP | 返回日期 UNIX 時間戳 |
| DATE_FORMAT | 返回按照字串格式化的日期 |
| FROM_UNIXTIME | 返回 UNIX 時間戳的日期值 |
| DATE_ADD | 返回日期時間 + 上一個時間間隔 |
| DATEDIFF | 返回起始時間和結束時間之間的天數 |
下面結合示例來講解一下每個函式的使用
* NOW(): 返回當前的日期和時間
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191253918-1526292639.png)
* WEEK(DATE) 和 YEAR(DATE) :前者返回的是一年中的第幾周,後者返回的是給定日期的哪一年
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191302742-1840072675.png)
* HOUR(time) 和 MINUTE(time) : 返回給定時間的小時,後者返回給定時間的分鐘
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191309666-329841314.png)
* MONTHNAME(date) 函式:返回 date 的英文月份
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191317786-1909780427.png)
* CURDATE() 函式:返回當前日期,只包含年月日
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191328117-1591627113.png)
* CURTIME() 函式:返回當前時間,只包含時分秒
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191334745-1209893325.png)
* UNIX_TIMESTAMP(date) : 返回 UNIX 的時間戳
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191341908-130213625.png)
* FROM_UNIXTIME(date) : 返回 UNIXTIME 時間戳的日期值,和 UNIX_TIMESTAMP 相反
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191349445-723883543.png)
* DATE_FORMAT(date,fmt) 函式:按照字串 fmt 對 date 進行格式化,格式化後按照指定日期格式顯示
具體的日期格式可以參考這篇文章 https://blog.csdn.net/weixin_38703170/article/details/82177837
我們演示一下將當前日期顯示為**年月日**的這種形式,使用的日期格式是 **%M %D %Y**。
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191357587-989325473.png)
* DATE_ADD(date, interval, expr type) 函式:返回與所給日期 date 相差 interval 時間段的日期
interval 表示間隔型別的關鍵字,expr 是表示式,這個表示式對應後面的型別,type 是間隔型別,MySQL 提供了 13 種時間間隔型別
| 表示式型別 | 描述 | 格式 |
| ------------- | -------- | --------------- |
| YEAR | 年 | YY |
| MONTH | 月 | MM |
| DAY | 日 | DD |
| HOUR | 小時 | hh |
| MINUTE | 分 | mm |
| SECOND | 秒 | ss |
| YEAR_MONTH | 年和月 | YY-MM |
| DAY_HOUR | 日和小時 | DD hh |
| DAY_MINUTE | 日和分鐘 | DD hh : mm |
| DAY_SECOND | 日和秒 | DD hh :mm :ss |
| HOUR_MINUTE | 小時和分 | hh:mm |
| HOUR_SECOND | 小時和秒 | hh:ss |
| MINUTE_SECOND | 分鐘和秒 | mm:ss |
* DATE_DIFF(date1, date2) 用來計算兩個日期之間相差的天數
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191407239-2014544934.png)
檢視離 2021 - 01 - 01 還有多少天
### 流程函式
流程函式也是很常用的一類函式,使用者可以使用這類函式在 SQL 中實現條件選擇。這樣做能夠提高查詢效率。下表列出了這些流程函式
| 函式 | 功能 |
| ----------------------------------------------------------- | ------------------------------------------------------- |
| IF(value,t f) | 如果 value 是真,返回 t;否則返回 f |
| IFNULL(value1,value2) | 如果 value1 不為 NULL,返回 value1,否則返回 value2。 |
| CASE WHEN[value1] THEN[result1] ...ELSE[default] END | 如果 value1 是真,返回 result1,否則返回 default |
| CASE[expr] WHEN[value1] THEN [result1]... ELSE[default] END | 如果 expr 等於 value1, 返回 result1, 否則返回 default |
### 其他函式
除了我們介紹過的字串函式、日期和時間函式、流程函式,還有一些函式並不屬於上面三類函式,它們是
| 函式 | 功能 |
| -------------- | ---------------------- |
| VERSION | 返回當前資料庫的版本 |
| DATABASE | 返回當前資料庫名 |
| USER | 返回當前登陸使用者名稱 |
| PASSWORD | 返回字串的加密版本 |
| MD5 | 返回 MD5 值 |
| INET_ATON(IP) | 返回 IP 地址的數字表示 |
| INET_NTOA(num) | 返回數字代表的 IP 地址 |
下面來看一下具體的使用
* VERSION: 返回當前資料庫版本
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191416707-2068012968.png)
* DATABASE: 返回當前的資料庫名
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191423533-702299399.png)
* USER : 返回當前登入使用者名稱
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191429628-580411417.png)
* PASSWORD(str) : 返回字串的加密版本,例如
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191436903-282181600.png)
* MD5(str) 函式:返回字串 str 的 MD5 值
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191443736-91163215.png)
* INET_ATON(IP): 返回 IP 的網路位元組序列
![](https://img2020.cnblogs.com/blog/1515111/202006/1515111-20200621191449939-1519184089.png)
* INET_NTOA(num)函式:返回網路位元組序列代表的 IP 地址,與 INET_ATON 相對
## 總結
這篇文章我帶你手把手擼了一波 MySQL 的高階內容,其實說高階也不一定真的高階或者說難,其實就是區分不同梯度的東西。
你的支援就是我擼文的動力!我是 cxuan,我們下篇文章見。
**另外,新增我的微信 becomecxuan,加入每日一題群,每天一道面試題分享,更多內容請參見我的 Github,[成為最好的 bestJavaer](https://github.com/crisxuan/bestJavaer)**。
**我自己肝了六本 PDF,微信搜尋「程式設計師cxuan」關注公眾號後,在後臺回覆 cxuan ,領取全部 PDF,這些 PDF 如下**
[六本 PDF 連結](https://s3.ax1x.com/2020/11/30/DgOK6f.png)
![](https://img2020.cnblogs.com/blog/1515111/202011/1515111-20201130090550310-10329982