南寧網(wǎng)站建設(shè) 南寧聯(lián)達(dá)億seo優(yōu)化網(wǎng)站源碼
數(shù)據(jù)庫(kù)
- 數(shù)據(jù)庫(kù)的三范式是什么
- drop、delete、truncate 分別在什么場(chǎng)景之下使用
- char 和 varchar 的區(qū)別是什么
- 數(shù)據(jù)庫(kù)的樂(lè)觀鎖和悲觀鎖是什么
- SQL 約束有哪幾種
- mysql 的內(nèi)連接、左連接、右連接有什么區(qū)別
- MyIASM和Innodb兩種引擎所使用的索引的數(shù)據(jù)結(jié)構(gòu)是什么
- mysql 有關(guān)權(quán)限的表都有哪幾個(gè)
- MySQL中InnoDB引擎的行鎖是通過(guò)加在什么上完成
- Oracle和Mysql的區(qū)別
- MyISAM和InnoDB區(qū)別
- 索引相關(guān)問(wèn)題
- 事務(wù)相關(guān)問(wèn)題
- SQL語(yǔ)句在MySQL中如何執(zhí)行
- SQL 優(yōu)化
數(shù)據(jù)庫(kù)的三范式是什么
- 第一范式:列不可再分
- 第二范式:屬性完全依賴于主鍵
- 第三范式:任何非主屬性不依賴于其它非主屬性
drop、delete、truncate 分別在什么場(chǎng)景之下使用
- 不再需要一張表的時(shí)候,用drop
- 想刪除部分?jǐn)?shù)據(jù)行時(shí)候,用delete,并且?guī)蟱here子句
- 保留表而刪除所有數(shù)據(jù)的時(shí)候用truncate
char 和 varchar 的區(qū)別是什么
Char是一種固定長(zhǎng)度的類型,varchar是一種可變長(zhǎng)度的類型
數(shù)據(jù)庫(kù)的樂(lè)觀鎖和悲觀鎖是什么
- 悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作
- 在查詢完數(shù)據(jù)的時(shí)候就把事務(wù)鎖起來(lái),直到提交事務(wù)
- 實(shí)現(xiàn)方式:使用數(shù)據(jù)庫(kù)中的鎖機(jī)制
- 樂(lè)觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。
- 在修改數(shù)據(jù)的時(shí)候把事務(wù)鎖起來(lái),通過(guò)version的方式來(lái)進(jìn)行鎖定
- 實(shí)現(xiàn)方式:使用version版本或者時(shí)間戳
SQL 約束有哪幾種
- 主鍵約束(非空,唯一)
- 外鍵約束(外鍵字段必須引用于主鍵字段,被引用的表稱為主表,引用的表稱為從表)
- 唯一約束
- 非空約束
- 檢查約束
mysql 的內(nèi)連接、左連接、右連接有什么區(qū)別
- 內(nèi)連接:顯示兩個(gè)表中有聯(lián)系的所有數(shù)據(jù)
- 左鏈接:以左表為參照,顯示左邊所有數(shù)據(jù),右表中沒(méi)有則以null顯示
- 右鏈接:以右表為參照,顯示右邊所有數(shù)據(jù),左表中沒(méi)有則以null顯示
MyIASM和Innodb兩種引擎所使用的索引的數(shù)據(jù)結(jié)構(gòu)是什么
都是B+樹(shù)
MyIASM引擎,B+樹(shù)的數(shù)據(jù)結(jié)構(gòu)中存儲(chǔ)的內(nèi)容實(shí)際上是實(shí)際數(shù)據(jù)的地址值。也就是說(shuō)它的索引和實(shí)際數(shù)據(jù)是分開(kāi)的,只不過(guò)使用索引指向了實(shí)際數(shù)據(jù)。這種索引的模式被稱為非聚集索引。
Innodb引擎的索引的數(shù)據(jù)結(jié)構(gòu)也是B+樹(shù),只不過(guò)數(shù)據(jù)結(jié)構(gòu)中存儲(chǔ)的都是實(shí)際的數(shù)據(jù),這種索引有被稱為聚集索引。
mysql 有關(guān)權(quán)限的表都有哪幾個(gè)
- user權(quán)限表:記錄允許連接到服務(wù)器的用戶帳號(hào)信息,里面的權(quán)限是全局級(jí)的。
- db權(quán)限表:記錄各個(gè)帳號(hào)在各個(gè)數(shù)據(jù)庫(kù)上的操作權(quán)限。
- table_priv權(quán)限表:記錄數(shù)據(jù)表級(jí)的操作權(quán)限。
- columns_priv權(quán)限表:記錄數(shù)據(jù)列級(jí)的操作權(quán)限。
- host權(quán)限表:配合db權(quán)限表對(duì)給定主機(jī)上數(shù)據(jù)庫(kù)級(jí)操作權(quán)限作更細(xì)致的控制。這個(gè)權(quán)限表不受GRANT和REVOKE語(yǔ)句的影響。
MySQL中InnoDB引擎的行鎖是通過(guò)加在什么上完成
InnoDB是基于索引來(lái)完成行鎖
Oracle和Mysql的區(qū)別
在Mysql中,一個(gè)用戶下可以創(chuàng)建多個(gè)庫(kù):
而在Oracle中,Oracle服務(wù)器是由兩部分組成
- 數(shù)據(jù)庫(kù)實(shí)例【理解為對(duì)象,看不見(jiàn)的】
- 數(shù)據(jù)庫(kù)【理解為類,看得見(jiàn)的】
一個(gè)數(shù)據(jù)庫(kù)實(shí)例可擁有多個(gè)用戶,一個(gè)用戶默認(rèn)擁有一個(gè)表空間。表空間是存儲(chǔ)我們數(shù)據(jù)庫(kù)表的地方,表空間內(nèi)可以有多個(gè)文件。
當(dāng)我們使用Oracle作為我們數(shù)據(jù)庫(kù)時(shí),我們需要指定用戶、表空間來(lái)存儲(chǔ)我們所需要的數(shù)據(jù)!
MyISAM和InnoDB區(qū)別
MyISAM是MySQL的默認(rèn)數(shù)據(jù)庫(kù)引擎(5.5版之前)。5.5版本之后,MySQL引入了InnoDB(事務(wù)性數(shù)據(jù)庫(kù)引擎),MySQL5.5版本后默認(rèn)的存儲(chǔ)引擎為InnoDB。
- 是否支持行級(jí)鎖: MyISAM 只有表級(jí)鎖,InnoDB 支持行級(jí)鎖和表級(jí)鎖,默認(rèn)為行級(jí)鎖。
- 是否支持事務(wù)和崩潰后的安全恢復(fù): MyISAM 強(qiáng)調(diào)的是性能,每次查詢具有原子性,其執(zhí)行速度比InnoDB類型更快,但是不提供事務(wù)支持。但是InnoDB 提供事務(wù)支持事務(wù),外部鍵等高級(jí)數(shù)據(jù)庫(kù)能。 是具有事務(wù)、回滾和崩潰修復(fù)能力的事務(wù)安全型表。
- 是否支持外鍵: MyISAM不支持,InnoDB支持。
- 是否支持MVCC :僅 InnoDB 支持。MVCC意思為多版本并發(fā)控制,應(yīng)對(duì)高并發(fā)事務(wù),MVCC比單純的加鎖更?效,MVCC只在讀已提交(RC) 和 可重復(fù)讀(RR) 兩個(gè)隔離級(jí)別下工作。用更好的方式去處理讀寫沖突。
索引相關(guān)問(wèn)題
https://blog.csdn.net/yy139926/article/details/124443946
事務(wù)相關(guān)問(wèn)題
https://blog.csdn.net/yy139926/article/details/124582264
SQL語(yǔ)句在MySQL中如何執(zhí)行
- 連接器
主要負(fù)責(zé)用戶登錄數(shù)據(jù)庫(kù),進(jìn)行用戶的身份認(rèn)證,包括校驗(yàn)賬戶密碼,權(quán)限等操作,如果用戶賬戶密碼已通過(guò),連接器會(huì)到權(quán)限表中查詢?cè)撚脩舻乃袡?quán)限,之后在這個(gè)連接里的權(quán)限邏輯判斷都是會(huì)依賴此時(shí)讀取到的權(quán)限數(shù)據(jù),也就是說(shuō),后續(xù)只要這個(gè)連接不斷開(kāi),即時(shí)管理員修改了該用戶的權(quán)限,該用戶也是不受影響的。
- 查詢緩存(MySQL 8.0 版本后移除)
查詢緩存主要用來(lái)緩存我們所執(zhí)行的 SELECT 語(yǔ)句以及該語(yǔ)句的結(jié)果集。
連接建立后,執(zhí)行查詢語(yǔ)句的時(shí)候,會(huì)先查詢緩存,MySQL 會(huì)先校驗(yàn)這個(gè) sql 是否執(zhí)行過(guò),以 Key-Value 的形式緩存在內(nèi)存中,Key 是查詢預(yù)計(jì),Value 是結(jié)果集。如果緩存 key 被命中,就會(huì)直接返回給客戶端,如果沒(méi)有命中,就會(huì)執(zhí)行后續(xù)的操作,完成后也會(huì)把結(jié)果緩存起來(lái),方便下一次調(diào)用。當(dāng)然在真正執(zhí)行緩存查詢的時(shí)候還是會(huì)校驗(yàn)用戶的權(quán)限,是否有該表的查詢條件。
MySQL 查詢不建議使用緩存,因?yàn)椴樵兙彺媸г趯?shí)際業(yè)務(wù)場(chǎng)景中可能會(huì)非常頻繁,假如你對(duì)一個(gè)表更新的話,這個(gè)表上的所有的查詢緩存都會(huì)被清空。對(duì)于不經(jīng)常更新的數(shù)據(jù)來(lái)說(shuō),使用緩存還是可以的。所以,一般在大多數(shù)情況下我們都是不推薦去使用查詢緩存的。
- 分析器
MySQL 沒(méi)有命中緩存,那么就會(huì)進(jìn)入分析器,分析器主要是用來(lái)分析 SQL 語(yǔ)句是來(lái)干嘛的,分析器也會(huì)分為幾步:
第一步,詞法分析,一條 SQL 語(yǔ)句有多個(gè)字符串組成,首先要提取關(guān)鍵字,比如 select,提出查詢的表,提出字段名,提出查詢條件等等。做完這些操作后,就會(huì)進(jìn)入第二步。
第二步,語(yǔ)法分析,主要就是判斷你輸入的 sql 是否正確,是否符合 MySQL 的語(yǔ)法。
完成這 2 步之后,MySQL 就準(zhǔn)備開(kāi)始執(zhí)行了,但是如何執(zhí)行,怎么執(zhí)行是最好的結(jié)果呢?這個(gè)時(shí)候就需要優(yōu)化器上場(chǎng)了。
- 優(yōu)化器
優(yōu)化器的作用就是它認(rèn)為的最優(yōu)的執(zhí)行方案去執(zhí)行,比如多個(gè)索引的時(shí)候該如何選擇索引,多表查詢的時(shí)候如何選擇關(guān)聯(lián)順序等。可以說(shuō),經(jīng)過(guò)了優(yōu)化器之后可以說(shuō)這個(gè)語(yǔ)句具體該如何執(zhí)行就已經(jīng)定下來(lái)。
- 執(zhí)行器
當(dāng)選擇了執(zhí)行方案后,MySQL 就準(zhǔn)備開(kāi)始執(zhí)行了,首先執(zhí)行前會(huì)校驗(yàn)該用戶有沒(méi)有權(quán)限,如果沒(méi)有權(quán)限,就會(huì)返回錯(cuò)誤信息,如果有權(quán)限,就會(huì)去調(diào)用引擎的接口,返回接口執(zhí)行的結(jié)果。
SQL 優(yōu)化
避免使用select *
很多時(shí)候,我們寫sql語(yǔ)句時(shí),為了方便,喜歡直接使用select *,一次性查出表中所有列的數(shù)據(jù)。
反例:
select * from user where id=1;
在實(shí)際業(yè)務(wù)場(chǎng)景中,可能我們真正需要使用的只有其中一兩列。查了很多數(shù)據(jù),但是不用,白白浪費(fèi)了數(shù)據(jù)庫(kù)資源,比如:內(nèi)存或者cpu。
此外,多查出來(lái)的數(shù)據(jù),通過(guò)網(wǎng)絡(luò)IO傳輸?shù)倪^(guò)程中,也會(huì)增加數(shù)據(jù)傳輸?shù)臅r(shí)間。
還有一個(gè)最重要的問(wèn)題是:select *不會(huì)走覆蓋索引,會(huì)出現(xiàn)大量的回表操作,而從導(dǎo)致查詢sql的性能很低。
那么,如何優(yōu)化呢?
正例:
select name,age from user where id=1;
用union all代替union
我們都知道sql語(yǔ)句使用union關(guān)鍵字后,可以獲取排重后的數(shù)據(jù)。
而如果使用union all關(guān)鍵字,可以獲取所有數(shù)據(jù),包含重復(fù)的數(shù)據(jù)。
反例:
(select * from user where id=1)
union
(select * from user where id=2);
排重的過(guò)程需要遍歷、排序和比較,它更耗時(shí),更消耗cpu資源。
所以如果能用union all的時(shí)候,盡量不用union。
正例:
(select * from user where id=1)
union all
(select * from user where id=2);
除非是有些特殊的場(chǎng)景,比如union all之后,結(jié)果集中出現(xiàn)了重復(fù)數(shù)據(jù),而業(yè)務(wù)場(chǎng)景中是不允許產(chǎn)生重復(fù)數(shù)據(jù)的,這時(shí)可以使用union。
小表驅(qū)動(dòng)大表,也就是說(shuō)用小表的數(shù)據(jù)集驅(qū)動(dòng)大表的數(shù)據(jù)集
假如有order和user兩張表,其中order表有10000條數(shù)據(jù),而user表有100條數(shù)據(jù)。
這時(shí)如果想查一下,所有有效的用戶下過(guò)的訂單列表。
可以使用in關(guān)鍵字實(shí)現(xiàn):
select * from order
where user_id in (select id from user where status=1)
也可以使用exists關(guān)鍵字實(shí)現(xiàn):
select * from order
where exists (select 1 from user where order.user_id = user.id and status=1)
前面提到的這種業(yè)務(wù)場(chǎng)景,使用in關(guān)鍵字去實(shí)現(xiàn)業(yè)務(wù)需求,更加合適。
為什么呢?
因?yàn)槿绻鹲ql語(yǔ)句中包含了in關(guān)鍵字,則它會(huì)優(yōu)先執(zhí)行in里面的子查詢語(yǔ)句,然后再執(zhí)行in外面的語(yǔ)句。如果in里面的數(shù)據(jù)量很少,作為條件查詢速度更快。
而如果sql語(yǔ)句中包含了exists關(guān)鍵字,它優(yōu)先執(zhí)行exists左邊的語(yǔ)句(即主查詢語(yǔ)句)。然后把它作為條件,去跟右邊的語(yǔ)句匹配。如果匹配上,則可以查詢出數(shù)據(jù)。如果匹配不上,數(shù)據(jù)就被過(guò)濾掉了。
這個(gè)需求中,order表有10000條數(shù)據(jù),而user表有100條數(shù)據(jù)。order表是大表,user表是小表。如果order表在左邊,則用in關(guān)鍵字性能更好。
總結(jié)一下:
- in 適用于左邊大表,右邊小表。
- exists 適用于左邊小表,右邊大表。
不管是用in,還是exists關(guān)鍵字,其核心思想都是用小表驅(qū)動(dòng)大表。
批量操作
如果你有一批數(shù)據(jù)經(jīng)過(guò)業(yè)務(wù)處理之后,需要插入數(shù)據(jù),該怎么辦?
反例:
for(Order order: list){orderMapper.insert(order):
}
在循環(huán)中逐條插入數(shù)據(jù)。
insert into order(id,code,user_id)
values(123,'001',100);
該操作需要多次請(qǐng)求數(shù)據(jù)庫(kù),才能完成這批數(shù)據(jù)的插入。
但眾所周知,我們?cè)诖a中,每次遠(yuǎn)程請(qǐng)求數(shù)據(jù)庫(kù),是會(huì)消耗一定性能的。而如果我們的代碼需要請(qǐng)求多次數(shù)據(jù)庫(kù),才能完成本次業(yè)務(wù)功能,勢(shì)必會(huì)消耗更多的性能。
那么如何優(yōu)化呢?
正例:
orderMapper.insertBatch(list):
提供一個(gè)批量插入數(shù)據(jù)的方法。
insert into order(id,code,user_id)
values(123,'001',100),(124,'002',100),(125,'003',101);
這樣只需要遠(yuǎn)程請(qǐng)求一次數(shù)據(jù)庫(kù),sql性能會(huì)得到提升,數(shù)據(jù)量越多,提升越大。
但需要注意的是,不建議一次批量操作太多的數(shù)據(jù),如果數(shù)據(jù)太多數(shù)據(jù)庫(kù)響應(yīng)也會(huì)很慢。批量操作需要把握一個(gè)度,建議每批數(shù)據(jù)盡量控制在500以內(nèi)。如果數(shù)據(jù)多于500,則分多批次處理。
多用limit
有時(shí)候,我們需要查詢某些數(shù)據(jù)中的第一條,比如:查詢某個(gè)用戶下的第一個(gè)訂單,想看看他第一次的首單時(shí)間。
select id, create_date from order
where user_id=123
order by create_date asc;
根據(jù)用戶id查詢訂單,按下單時(shí)間排序,先查出該用戶所有的訂單數(shù)據(jù),得到一個(gè)訂單集合。然后在代碼中,獲取第一個(gè)元素的數(shù)據(jù),即首單的數(shù)據(jù),就能獲取首單時(shí)間。
List<Order> list = orderMapper.getOrderList();
Order order = list.get(0);
雖說(shuō)這種做法在功能上沒(méi)有問(wèn)題,但它的效率非常不高,需要先查詢出所有的數(shù)據(jù),有點(diǎn)浪費(fèi)資源。
那么,如何優(yōu)化呢?
正例:
select id, create_date from order
where user_id=123
order by create_date asc
limit 1;
使用limit 1,只返回該用戶下單時(shí)間最小的那一條數(shù)據(jù)即可。此外,在刪除或者修改數(shù)據(jù)時(shí),為了防止誤操作,導(dǎo)致刪除或修改了不相干的數(shù)據(jù),也可以在sql語(yǔ)句最后加上limit。
例如:
update order set status=0,edit_time=now(3)
where id>=100 and id<200 limit 100;
這樣即使誤操作,比如把id搞錯(cuò)了,也不會(huì)對(duì)太多的數(shù)據(jù)造成影響。
SQL查找是否"存在",別再count了
根據(jù)某一條件從數(shù)據(jù)庫(kù)表中查詢 『有』與『沒(méi)有』,只有兩種狀態(tài),那為什么在寫SQL的時(shí)候,還要SELECT count(*) 呢?
無(wú)論是剛?cè)氲赖某绦騿T新星,還是精湛沙場(chǎng)多年的程序員老白,都是一如既往的count
反例:目前多數(shù)人的寫法
多次REVIEW代碼時(shí),發(fā)現(xiàn)如現(xiàn)現(xiàn)象:
業(yè)務(wù)代碼中,需要根據(jù)一個(gè)或多個(gè)條件,查詢是否存在記錄,不關(guān)心有多少條記錄。普遍的SQL及代碼寫法如下
#### SQL寫法:
SELECT count(*) FROM table WHERE a = 1 AND b = 2#### Java寫法:
int nums = xxDao.countXxxxByXxx(params);
if ( nums > 0 ) {//當(dāng)存在時(shí),執(zhí)行這里的代碼
} else {//當(dāng)不存在時(shí),執(zhí)行這里的代碼
}
推薦寫法如下:
#### SQL寫法:
SELECT 1 FROM table WHERE a = 1 AND b = 2 LIMIT 1#### Java寫法:
Integer exist = xxDao.existXxxxByXxx(params);
if ( exist != NULL ) {//當(dāng)存在時(shí),執(zhí)行這里的代碼
} else {//當(dāng)不存在時(shí),執(zhí)行這里的代碼
}
根據(jù)查詢條件查出來(lái)的條數(shù)越多,性能提升的越明顯,在某些情況下,還可以減少聯(lián)合索引的創(chuàng)建。
in中值太多
對(duì)于批量查詢接口,我們通常會(huì)使用in關(guān)鍵字過(guò)濾出數(shù)據(jù)。比如:想通過(guò)指定的一些id,批量查詢出用戶信息。
sql語(yǔ)句如下:
select id,name from category
where id in (1,2,3...100000000);
如果我們不做任何限制,該查詢語(yǔ)句一次性可能會(huì)查詢出非常多的數(shù)據(jù),很容易導(dǎo)致接口超時(shí)。
這時(shí)該怎么辦呢?
select id,name from category
where id in (1,2,3...100)
limit 500;
可以在sql中對(duì)數(shù)據(jù)用limit做限制。
不過(guò)我們更多的是要在業(yè)務(wù)代碼中加限制,偽代碼如下:
public List<Category> getCategory(List<Long> ids) {if(CollectionUtils.isEmpty(ids)) {return null;}if(ids.size() > 500) {throw new BusinessException("一次最多允許查詢500條記錄")}return mapper.getCategoryList(ids);
}
還有一個(gè)方案就是:如果ids超過(guò)500條記錄,可以分批用多線程去查詢數(shù)據(jù)。每批只查500條記錄,最后把查詢到的數(shù)據(jù)匯總到一起返回。
不過(guò)這只是一個(gè)臨時(shí)方案,不適合于ids實(shí)在太多的場(chǎng)景。因?yàn)閕ds太多,即使能快速查出數(shù)據(jù),但如果返回的數(shù)據(jù)量太大了,網(wǎng)絡(luò)傳輸也是非常消耗性能的,接口性能始終好不到哪里去。
高效的分頁(yè)
有時(shí)候,列表頁(yè)在查詢數(shù)據(jù)時(shí),為了避免一次性返回過(guò)多的數(shù)據(jù)影響接口性能,我們一般會(huì)對(duì)查詢接口做分頁(yè)處理。
在mysql中分頁(yè)一般用的limit關(guān)鍵字:
select id,name,age
from user limit 10,20;
如果表中數(shù)據(jù)量少,用limit關(guān)鍵字做分頁(yè),沒(méi)啥問(wèn)題。但如果表中數(shù)據(jù)量很多,用它就會(huì)出現(xiàn)性能問(wèn)題。
比如現(xiàn)在分頁(yè)參數(shù)變成了:
select id,name,age
from user limit 1000000,20;
mysql會(huì)查到1000020條數(shù)據(jù),然后丟棄前面的1000000條,只查后面的20條數(shù)據(jù),這個(gè)是非常浪費(fèi)資源的。
那么,這種海量數(shù)據(jù)該怎么分頁(yè)呢?
優(yōu)化sql:
select id,name,age
from user where id > 1000000 limit 20;
先找到上次分頁(yè)最大的id,然后利用id上的索引查詢。不過(guò)該方案,要求id是連續(xù)的,并且有序的。
還能使用between優(yōu)化分頁(yè)。
select id,name,age
from user where id between 1000000 and 1000020;
需要注意的是between要在唯一索引上分頁(yè),不然會(huì)出現(xiàn)每頁(yè)大小不一致的問(wèn)題。
用連接查詢代替子查詢
mysql中如果需要從兩張以上的表中查詢出數(shù)據(jù)的話,一般有兩種實(shí)現(xiàn)方式:子查詢 和 連接查詢。
子查詢的例子如下:
select * from order
where user_id in (select id from user where status=1)
子查詢語(yǔ)句可以通過(guò)in關(guān)鍵字實(shí)現(xiàn),一個(gè)查詢語(yǔ)句的條件落在另一個(gè)select語(yǔ)句的查詢結(jié)果中。程序先運(yùn)行在嵌套在最內(nèi)層的語(yǔ)句,再運(yùn)行外層的語(yǔ)句。
子查詢語(yǔ)句的優(yōu)點(diǎn)是簡(jiǎn)單,結(jié)構(gòu)化,如果涉及的表數(shù)量不多的話。
但缺點(diǎn)是mysql執(zhí)行子查詢時(shí),需要?jiǎng)?chuàng)建臨時(shí)表,查詢完畢后,需要再刪除這些臨時(shí)表,有一些額外的性能消耗。
這時(shí)可以改成連接查詢。具體例子如下:
select o.* from order o
inner join user u on o.user_id = u.id
where u.status=1
join時(shí)要注意
我們?cè)谏婕暗蕉鄰埍砺?lián)合查詢的時(shí)候,一般會(huì)使用join關(guān)鍵字。
而join使用最多的是left join和inner join。
left join:求兩個(gè)表的交集外加左表剩下的數(shù)據(jù)。
inner join:求兩個(gè)表交集的數(shù)據(jù)。
使用inner join的示例如下:
select o.id,o.code,u.name
from order o
inner join user u on o.user_id = u.id
where u.status=1;
如果兩張表使用inner join關(guān)聯(lián),mysql會(huì)自動(dòng)選擇兩張表中的小表,去驅(qū)動(dòng)大表,所以性能上不會(huì)有太大的問(wèn)題。
使用left join的示例如下:
select o.id,o.code,u.name
from order o
left join user u on o.user_id = u.id
where u.status=1;
如果兩張表使用left join關(guān)聯(lián),mysql會(huì)默認(rèn)用left join關(guān)鍵字左邊的表,去驅(qū)動(dòng)它右邊的表。如果左邊的表數(shù)據(jù)很多時(shí),就會(huì)出現(xiàn)性能問(wèn)題。
要特別注意的是在用left join關(guān)聯(lián)查詢時(shí),左邊要用小表,右邊可以用大表。如果能用inner join的地方,盡量少用left join。
提升group by的效率
我們有很多業(yè)務(wù)場(chǎng)景需要使用group by關(guān)鍵字,它主要的功能是去重和分組。
通常它會(huì)跟having一起配合使用,表示分組后再根據(jù)一定的條件過(guò)濾數(shù)據(jù)。
反例:
select user_id,user_name from order
group by user_id
having user_id <= 200;
這種寫法性能不好,它先把所有的訂單根據(jù)用戶id分組之后,再去過(guò)濾用戶id大于等于200的用戶。
分組是一個(gè)相對(duì)耗時(shí)的操作,為什么我們不先縮小數(shù)據(jù)的范圍之后,再分組呢?
正例:
select user_id,user_name from order
where user_id <= 200
group by user_id
使用where條件在分組前,就把多余的數(shù)據(jù)過(guò)濾掉了,這樣分組時(shí)效率就會(huì)更高一些。
其實(shí)這是一種思路,不僅限于group by的優(yōu)化。我們的sql語(yǔ)句在做一些耗時(shí)的操作之前,應(yīng)盡可能縮小數(shù)據(jù)范圍,這樣能提升sql整體的性能。