亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

升級到MySQL5.7后開發不得不注意的一些坑

2024-07-25 19:08:38
字體:
來源:轉載
供稿:網友

前言

前段時間,將線上MySQL數據庫升級到了5.7。考慮到可能產生的不兼容性,在升級之前,確實也是戰戰兢兢,雖然測試環境,開發環境早在半年前就已提前升級。

基于前期的調研和朋友的反饋,與開發相關的主要有兩點:

sql_mode

MySQL 5.6中,其默認值為"NO_ENGINE_SU BSTITUTION",可理解為非嚴格模式,譬如,對自增主鍵插入空字符串'',雖然提示warning,但并不影響自增主鍵的生成。

但在MySQL 5.7中,其就調整為了嚴格模式,對于上面這個,其不會提示warning,而是直接報錯。

分組求最值

分組求最值的某些寫法在MySQL5.7中得不到預期結果,這點,相對來說比較隱蔽。

其中,第一點是可控的,畢竟可以調整參數。而第二點,卻是不可控的,沒有參數與之相關,需要開發Review代碼。

下面具體來看看

測試數據

mysql> select * from emp;+-------+----------+--------+--------+| empno | ename | sal | deptno |+-------+----------+--------+--------+| 1001 | emp_1001 | 100.00 | 10 || 1002 | emp_1002 | 200.00 | 10 || 1003 | emp_1003 | 300.00 | 20 || 1004 | emp_1004 | 400.00 | 20 || 1005 | emp_1005 | 500.00 | 30 || 1006 | emp_1006 | 600.00 | 30 |+-------+----------+--------+--------+rows in set (0.00 sec)

其中,empno是員工編號,ename是員工姓名,sal是工資,deptno是員工所在部門號。

業務的需求是,求出每個部門中工資最高的員工的相關信息。

在MySQL5.6中,我們可以通過下面這個SQL來實現,

SELECT deptno,ename,sal FROM ( SELECT * FROM emp ORDER BY sal DESC ) t GROUP BY deptno;

結果如下,可以看到,其確實實現了預期效果。

+--------+----------+--------+| deptno | ename | sal |+--------+----------+--------+| 10 | emp_1002 | 200.00 || 20 | emp_1004 | 400.00 || 30 | emp_1006 | 600.00 |+--------+----------+--------+

再來看看MySQL5.7的結果,竟然不一樣。

+--------+----------+--------+| deptno | ename | sal |+--------+----------+--------+| 10 | emp_1001 | 100.00 || 20 | emp_1003 | 300.00 || 30 | emp_1005 | 500.00 |+--------+----------+--------+

實際上,在MySQL5.7中,對該SQL進行了改寫,改寫后的SQL可通過explain(extended) + show warnings查看。

mysql> explain select deptno,ename,sal from (select * from emp order by sal desc) t group by deptno;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using temporary |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+row in set, 1 warning (0.00 sec)mysql> show warnings/G*************************** 1. row *************************** Level: Note Code: 1003Message: /* select#1 */ select `slowtech`.`emp`.`deptno` AS `deptno`,`slowtech`.`emp`.`ename` AS `ename`,`slowtech`.`emp`.`sal` AS `sal` from `slowtech`.`emp` group by `slowtech`.`emp`.`deptno`row in set (0.00 sec)

從改寫后的SQL來看,其消除了子查詢,導致結果未能實現預期效果,官方也證實了這一點,https://bugs.mysql.com/bug.php?id=80131

 

很多人可能不以為然,認為沒人會這樣寫,但在大名鼎鼎的stackoverflow中,該實現的點贊數就有116個-由此可見其受眾之廣,僅次于后面提到的“方法二”(點贊數206個)。 

https://stackoverflow.com/questions/12102200/get-records-with-max-value-for-each-group-of-grouped-sql-results

需要注意的是,該SQL在5.7中是不能直接運行的,其會提示如下錯誤:

ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 't.ename' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

這個與sql_mode有關,在MySQL 5.7中,sql_mode調整為了

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

其中,ONLY_FULL_GROUP_BY與group by語句有關,其要求select列表里只能出現分組列(即group by后面的列)和聚合函數(sum,avg,max等),這也是SQL92的標準。

但在工作中,卻經??吹介_發寫出下面這種SQL。

mysql> select deptno,ename,max(sal) from emp group by deptno;+--------+----------+----------+| deptno | ename | max(sal) |+--------+----------+----------+| 10 | emp_1001 | 200.00 || 20 | emp_1003 | 400.00 || 30 | emp_1005 | 600.00 |+--------+----------+----------+rows in set (0.01 sec)

 實在不明白,這里的ename在業務層有何意義,畢竟,他并不是工資最高的那位員工。 

分組求最值,MySQL的實現方式

其實分組求最值是一個很普遍的需求。在工作中,也經常被開發同事問到。 下面具體來看看,MySQL中有哪些實現方式。

方法1

SELECT e.deptno, ename, sal FROM emp e, ( SELECT deptno, max( sal ) maxsal FROM emp GROUP BY deptno ) t WHERE e.deptno = t.deptno  AND e.sal = t.maxsal;

方法2

SELECT a.deptno, a.ename, a.sal FROM emp a LEFT JOIN emp b ON a.deptno = b.deptno  AND a.sal < b.sal WHERE b.sal IS NULL;

這兩種實現方式,其實是通用的,不僅適用于MySQL,也適用于其它主流關系型數據庫。

方法3

MySQL 8.0推出了分析函數,其也可實現類似功能。

SELECT deptno, ename, sal FROM ( SELECT deptno, ename, sal, LAST_VALUE ( sal ) OVER ( PARTITION BY deptno ORDER BY sal ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) maxsal  FROM emp  ) a WHERE sal = maxsal;

三種實現方式的性能對比

因上面測試案例的數據量太小,三種實現方式的結果都是秒出,僅憑執行計劃很難直觀地看出實現方式的優劣。

下面換上數據量更大的測試數據,官方示例數據庫employees中的dept_emp表,https://github.com/datacharmer/test_db

表的相關信息如下,其中emp_no是員工編號,dept_no是部門編號,from_date是入職日期。

mysql> show create table dept_emp/G*************************** 1. row *************************** Table: dept_empCreate Table: CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, KEY `dept_no` (`dept_no`,`from_date`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_cirow in set (0.00 sec)mysql> select count(*) from dept_emp;+----------+| count(*) |+----------+| 331603 |+----------+row in set (0.09 sec)mysql> select * from dept_emp limit 1;+--------+---------+------------+------------+| emp_no | dept_no | from_date | to_date |+--------+---------+------------+------------+| 10001 | d005 | 1986-06-26 | 9999-01-01 |+--------+---------+------------+------------+row in set (0.00 sec)

方法1

mysql> select d.dept_no,d.emp_no,d.from_date from dept_emp d, (select dept_no,max(from_date) max_hiredate from dept_emp group by dept_no) t where d.dept_no=t.dept_no and d.from_date=t.max_hiredate;…rows in set (0.00 sec)mysql> explain select d.dept_no,d.emp_no,d.from_date from dept_emp d, (select dept_no,max(from_date) max_hiredate from dept_emp group by dept_no) t where d.dept_no=t.dept_no and d.from_date=t.max_hiredate;+----+-------------+------------+------------+-------+---------------+---------+---------+--------------------------+------+----------+----------------------| id | select_type | table | partitions | type | possible_keys | key | key_len | ref   | rows | filtered | Extra  +----+-------------+------------+------------+-------+---------------+---------+---------+--------------------------+------+----------+----------------------| 1 | PRIMARY | <derived2> | NULL | ALL | NULL  | NULL | NULL | NULL   | 9 | 100.00 | Using where  | 1 | PRIMARY | d  | NULL | ref | dept_no | dept_no | 19 | t.dept_no,t.max_hiredate | 5 | 100.00 | NULL   | 2 | DERIVED | dept_emp | NULL | range | dept_no | dept_no | 16 | NULL   | 9 | 100.00 | Using index for group-by+----+-------------+------------+------------+-------+---------------+---------+---------+--------------------------+------+----------+----------------------

方法2

mysql> explain select a.dept_no,a.emp_no,a.from_date from dept_emp a left join dept_emp b on a.dept_no=b.dept_no and a.from_date < b.from_date where b.from_date is null;+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+--------------------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref  | rows | filtered | Extra   |+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+--------------------------+| 1 | SIMPLE | a | NULL | ALL | NULL  | NULL | NULL | NULL  | 331008 | 100.00 | NULL   || 1 | SIMPLE | b | NULL | ref | dept_no | dept_no | 16 | slowtech.a.dept_no | 41376 | 19.00 | Using where; Using index |+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+--------------------------+rows in set, 1 warning (0.00 sec)

方法3

mysql> select dept_no,emp_no,from_date from ( select dept_no,emp_no,from_date,last_value(from_date) over(partition by dept_no order by from_date rows between unbounded preceding and unbounded following) max_hiredate from dept_emp) a where from_date=max_hiredate;…rows in set (1.57 sec)mysql> desc select dept_no,emp_no,from_date from ( select dept_no,emp_no,from_date,last_value(from_date) over(partition by dept_no order by from_date rows between unbounded preceding and unbounded following) max_hiredate from dept_emp) a where from_date=max_hiredate;+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+----------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra  |+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+----------------+| 1 | PRIMARY | <derived2> | NULL | ALL | NULL  | NULL | NULL | NULL | 331008 | 100.00 | Using where || 2 | DERIVED | dept_emp | NULL | ALL | NULL  | NULL | NULL | NULL | 331008 | 100.00 | Using filesort |+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+----------------+rows in set, 2 warnings (0.00 sec)

從執行時間上看,

方法1的時間最短,在有復合索引(deptno, fromdate)的情況下,結果瞬間就出來了,即使在沒有索引的情況下,也只消耗了0.75s。

方法2的時間最長,3個小時還是沒出結果。同樣的數據,同樣的SQL,放到Oracle查,也消耗了87分49秒。

方法3的時間比較固定,無論是否存在索引,都維持在1.5s左右,比方法1的耗時要久。

這里,對之前提到的,MySQL 5.7中不再兼容的實現方式也做了個測試,在沒有任何索引的情況下,其穩定在0.7s(性能并不弱,怪不得有人使用),而同等情況下,方法1穩定在0.5s(哈,MySQL 5.6竟然比8.0還快)。但與方法1不同的是,其無法通過索引進行優化。

從執行計劃上看,

方法1, 先將group by的結果放到臨時表中,然后再將該臨時表作為驅動表,來和dept_emp表進行關聯查詢。驅動表?。ㄖ挥?條記錄),關聯列又有索引,無怪乎,結果能秒出。

方法2, 兩表關聯。其犯了SQL優化中的兩個大忌。

   1. 驅動表太大,其有331603條記錄。

   2. 被驅動表雖然也有索引,但從執行計劃上看,其只使用了復合索引  (dept_no, from_date)中的dept_no,而dept_no的選擇率又太低,畢竟只有9個部門。

方法3, 先把分析的結果放到一個臨時表中,然后再對該臨時表進行處理。其進行了兩次全表掃描,一次是針對dept_emp表,一次是針對臨時表。

所以,對于分組求最值的需求,建議使用方法1,其不僅符合SQL規范,查詢性能上也是最好的,尤其是在聯合索引的情況下。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到MYSQL教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品第三页| 在线日韩精品视频| 欧美一区二区.| 欧美性极品少妇精品网站| 久久久久国产视频| 日韩欧美国产视频| 精品国产欧美成人夜夜嗨| 亚洲成人激情视频| 国产欧美日韩中文字幕| 韩国一区二区电影| 国产精品久久久久久搜索| 亚洲国产欧美日韩精品| 亚洲成av人片在线观看香蕉| 日韩最新免费不卡| 欧美性高潮床叫视频| 欧美亚洲国产视频小说| 色樱桃影院亚洲精品影院| 久久99精品久久久久久琪琪| 久久久久久久色| 欧美性猛交xxxx乱大交3| 欧美激情一区二区久久久| 日韩在线观看成人| 在线观看日韩专区| 亚洲色图在线观看| 91午夜理伦私人影院| 国产亚洲一级高清| 亚洲午夜性刺激影院| 国产精品视频网站| 丝袜亚洲欧美日韩综合| 亚洲精品黄网在线观看| 国产欧美日韩中文字幕在线| 亚洲第一网站免费视频| 国产亚洲精品久久久久久牛牛| 国内精品久久久久影院优| 日本成人精品在线| 亚洲午夜精品久久久久久久久久久久| 色综合天天综合网国产成人网| 成人激情av在线| 国产精品热视频| 国产精品18久久久久久麻辣| 久久久人成影片一区二区三区| 国内精品久久久久久久久| 日韩激情片免费| 欧美乱妇40p| 亚洲日本欧美日韩高观看| 日韩精品视频免费在线观看| 国产精品扒开腿做爽爽爽的视频| 亚洲精品免费网站| 亚洲精品久久视频| 亚洲二区在线播放视频| 国产日韩欧美成人| 成人网在线免费观看| 国产伦精品免费视频| 欧美性猛交xxxx偷拍洗澡| 亚洲精品黄网在线观看| 国产一区二区视频在线观看| 久久99国产精品久久久久久久久| 久久亚洲欧美日韩精品专区| 久久露脸国产精品| 怡红院精品视频| 日韩精品视频免费| 国产精品一区二区三区久久| 久久精品国产电影| 欧美激情欧美激情在线五月| 成人免费视频在线观看超级碰| 国产日韩在线亚洲字幕中文| 亚洲精品视频播放| 国产精品免费一区豆花| 成人免费在线视频网站| 91九色视频导航| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美日韩美女在线观看| 成人免费观看49www在线观看| 国产欧美va欧美va香蕉在线| 久久在线免费观看视频| 久久久精品999| 亚洲人成网站色ww在线| 成人国产在线激情| 国产精品一二区| 欧美成人激情在线| 欧美成人精品激情在线观看| 久久久久久尹人网香蕉| 日韩av在线导航| 久久激情五月丁香伊人| 亚洲人成网站999久久久综合| 国产精品亚洲第一区| 92版电视剧仙鹤神针在线观看| 91久久精品久久国产性色也91| 欧美午夜精品久久久久久浪潮| 欧美成人自拍视频| 成人精品在线观看| 欧美大片在线免费观看| 日韩视频欧美视频| 亚洲男人的天堂在线| 中文字幕久精品免费视频| 4438全国亚洲精品在线观看视频| 亚洲国产精品人久久电影| 午夜免费在线观看精品视频| 国产视频精品一区二区三区| 久久久午夜视频| 久久99久国产精品黄毛片入口| 精品久久久久久国产| 久久天天躁日日躁| 久久久999国产| 国产视频久久久久久久| 免费97视频在线精品国自产拍| 亚洲国产精品va在看黑人| 国产偷亚洲偷欧美偷精品| 亚洲精品欧美日韩专区| 国产小视频国产精品| 北条麻妃一区二区三区中文字幕| 欧美日韩在线一区| 亚洲性视频网址| 美女久久久久久久| 欧美性视频精品| 亚洲www在线| 亚洲小视频在线观看| 午夜精品久久久久久久99热| 日本中文字幕久久看| 亚洲欧美www| 一区二区三区久久精品| 欧美亚洲国产视频小说| 91色在线视频| 欧美激情视频一区二区三区不卡| 日本精品视频在线播放| 国产精品女人网站| 136fldh精品导航福利| 国内揄拍国内精品少妇国语| 国产欧美精品一区二区三区介绍| 57pao成人永久免费视频| 欧美色视频日本高清在线观看| 久久久久女教师免费一区| 亚洲黄页网在线观看| 98精品国产自产在线观看| 日本久久久a级免费| 日韩欧美在线观看| 国产成人精品综合| 亚洲日本中文字幕| 亚洲欧美国产精品久久久久久久| 国产免费一区视频观看免费| 欧美亚洲国产成人精品| 91免费看片在线| 日本伊人精品一区二区三区介绍| 欧美日韩国产一区二区| 日韩一级黄色av| 性色av香蕉一区二区| 久久久久久久久久久国产| 久久久久久国产精品三级玉女聊斋| 91久久久久久| 欧美性受xxxx白人性爽| 中文字幕欧美精品日韩中文字幕| 国产精品美女视频网站| 国产精品免费一区豆花| 九九热在线精品视频| 亚洲xxx大片| 欧美精品电影在线| 亚洲电影免费观看高清完整版| 91高潮精品免费porn| 狠狠色狠狠色综合日日小说| 亚洲亚裔videos黑人hd| 国产精品ⅴa在线观看h| 日韩av成人在线观看| 亚洲激情 国产| 欧美高清在线视频观看不卡|