通用表達式在各個商業數據庫中好比ORACLE,SQL SERVER等早就實現了,MySQL到了8.0 才支持這個特性。這裏有兩個方面來舉例說明WITH的好處。mysql
第一,易用性。sql
第二,效率。數據庫
咱們第一個例子, 對比視圖的檢索和WITH的檢索。咱們知道視圖在MySQL裏面的效率一直較差,雖然說MySQL5.7 對視圖作了相關固化的優化,不過依然不盡人意。考慮下,若是屢次在同一條SQL中訪問視圖,那麼則會屢次固化視圖,勢必增長相應的資源消耗。併發
MySQL裏以前對這種消耗的減小隻有一種,就是動態處理,不過一直語法較爲噁心,使用不是很廣。app
MySQL8.0後,又有了一種減小消耗的方式,就是WITH表達式。咱們假設如下表結構:ide
| CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `rank1` int(11) DEFAULT NULL, `rank2` int(11) DEFAULT NULL, `log_time` datetime DEFAULT NULL, `prefix_uid` varchar(100) DEFAULT NULL, `desc1` text, PRIMARY KEY (`id`), KEY `idx_rank1` (`rank1`), KEY `idx_rank2` (`rank2`), KEY `idx_log_time` (`log_time`)) ENGINE=InnoDB AUTO_INCREMENT=2037 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
有1000行測試記錄。函數
mysql> select count(*) from t1;+----------+| count(*) |+----------+| 1024 |+----------+1 row in set (0.00 sec)
這裏咱們創建一個普通的視圖:性能
CREATE ALGORITHM=merge SQL SECURITY DEFINER VIEW `v_t1_20` ASselect count(0) AS `cnt`,`t1`.`rank1` AS `rank1` from `t1` group by `t1`.`rank1` order by `t1`.`rank1` limit 20;
檢索語句A:測試
對視圖裏的最大和最小值字段rank1進行過濾檢索出符合條件的記錄行數。大數據
mysql> use ytt;mysql> SELECT COUNT(*)FROM t1WHERE rank1 = (SELECT MAX(rank1) FROM v_t1_20) OR rank1 = (SELECT MIN(rank1) FROM v_t1_20);+----------+| count(*) |+----------+| 65 |+----------+1 row in set (0.00 sec)
咱們用WITH表達式來重寫一遍這個查詢。
查詢語句B:
mysql> use ytt;mysql> with w_t1_20(cnt,rank1) as (SELECT COUNT(*), rank1FROM t1GROUP BY rank1ORDER BY rank1LIMIT 20) SELECT COUNT(*)FROM t1WHERE rank1 = (SELECT MAX(rank1) FROM w_t1_20) OR rank1 = (SELECT MIN(rank1) FROM w_t1_20);+----------+| count(*) |+----------+| 65 |+----------+1 row in set (0.00 sec)
個人函數不多,僅做功能性演示, 索引表面上看執行時間差很少, 咱們來對比下兩條實現語句的查詢計劃,
A的計劃:
B的計劃:
從以上圖咱們能夠看出,B比A少了一次對視圖的固化,也就是說,無論我訪問WITH多少次,僅僅固化一次。有興趣的能夠加大數據量,加大併發測試下性能。
咱們第二個例子,簡單說功能性。
好比以前MySQL一直存在的一個問題,就是臨時表不能打開屢次。咱們之前只有一種解決辦法就是把臨時表固化到磁盤,像訪問普通表那樣訪問臨時表。如今咱們能夠用MySQL8.0自帶的WITH表達式來作這樣的業務。
好比如下臨時表:
create temporary table ytt_tmp1 as SELECT COUNT(*) cnt, rank1FROM t1GROUP BY rank1ORDER BY rank1LIMIT 20
咱們仍是用以前的查詢,這裏會提示錯誤。
mysql> select count(*) from t1 where rank1 = (select max(rank1) from ytt_tmp1) or rank1 = (select min(rank1) from ytt_tmp1) -> ;ERROR 1137 (HY000): Can't reopen table: 'ytt_tmp1'
如今咱們能夠用WITH來改變這種思路。
mysql> use ytt;mysql> with w_t1_20(cnt,rank1) as (SELECT COUNT(*), rank1FROM t1GROUP BY rank1ORDER BY rank1LIMIT 20) SELECT COUNT(*)FROM t1WHERE rank1 = (SELECT MAX(rank1) FROM w_t1_20) OR rank1 = (SELECT MIN(rank1) FROM w_t1_20);+----------+| count(*) |+----------+| 65 |+----------+1 row in set (0.00 sec)
固然WITH的用法還有不少,感興趣的能夠去看看手冊上的更深刻的內容。