union和union all的性能差別居然這么大??!
最近做個(gè)項(xiàng)目,要把我們系統(tǒng)中的用戶數(shù)據(jù),按照別人系統(tǒng)的格式要求生成數(shù)據(jù)文本,導(dǎo)入到別的系統(tǒng)中。
我們系統(tǒng)的用戶數(shù)據(jù)主要存放在兩個(gè)表中,用戶信息表(user)和用戶附加信息表(extend)。user表中主要是用戶id,用戶昵稱、用戶名等信息,extend表中主要是手機(jī)號(hào)、身份證、核心客戶號(hào)碼等信息。每個(gè)表的主鍵都是用戶id(字段名為uid),分別有1.6億數(shù)據(jù),關(guān)聯(lián)查詢導(dǎo)出即可。
查詢要求
核心客戶號(hào)(cusnum)有效的用戶,即不為空、不為零。
核心客戶號(hào)(cusnum)為空,身份三要素(姓名name、證件類型certType、證件號(hào)碼certNum)有效的情況。
SQL語句
查詢SQL非常簡(jiǎn)單,分別將滿足條件的數(shù)據(jù)查出來做個(gè)合并,導(dǎo)出至指定文件即可。
// 核心客戶號(hào)有效
select ...
from user u inner join extend e
on u.uid=e.uid
where e.cusnum IS NOT NULL and e.cusnum != '' and e.cusnum != 0
union
// 核心客戶號(hào)為空,身份三要素有效
select ...
from user u inner join extend e
on u.uid=e.uid
where (e.cusnum IS NULL or e.cusnum = '' or e.cusnum = 0)
and (e.name IS NOT NULL and e.name != '')
and (e.certType IS NOT NULL and e.certType != '')
and (e.certNum IS NOT NULL and e.certNum != '')
into outfile '/xxxxx/xxxx.txt'但是使用這個(gè)SQL,導(dǎo)出全部1.6億的數(shù)據(jù)竟然用了14個(gè)小時(shí)?。。?/span>
這個(gè)時(shí)間已經(jīng)遠(yuǎn)遠(yuǎn)超過了投產(chǎn)的時(shí)間窗口,1.6億數(shù)據(jù)雖然會(huì)慢,但沒道理會(huì)這么慢呀。
把兩個(gè)SQL分別執(zhí)行導(dǎo)出,每個(gè)語句執(zhí)行了不到20分鐘就完成了,但是使用union在一起竟然會(huì)變得這么慢。
union機(jī)制
隨后,我就查了查mysql的union機(jī)制,才知道union不僅對(duì)多個(gè)sql的查詢結(jié)果做了合并,還在合并的基礎(chǔ)上做了默認(rèn)排序,同時(shí)還去除了重復(fù)行。1.6億的數(shù)據(jù)進(jìn)行排序、歸并,想想就頭疼。
如果不對(duì)查詢結(jié)果進(jìn)行排序、去重的話,可以使用union all。
union all 只是簡(jiǎn)單的將兩個(gè)結(jié)果合并后就返回。如果返回的兩個(gè)結(jié)果集中有重復(fù)的數(shù)據(jù),那么返回的結(jié)果集就會(huì)包含重復(fù)的數(shù)據(jù)了。
在我們的這個(gè)場(chǎng)景中,第一個(gè)語句的條件是cusnum有效,第二個(gè)語句的查詢條件是cusnum無效,因此不會(huì)有重復(fù)的數(shù)據(jù)。而且我們只要把數(shù)據(jù)全部導(dǎo)出即可,不關(guān)注數(shù)據(jù)的順序,因此我們可以用union all來替換union。
修改后再次執(zhí)行,這次只花費(fèi)不到30分鐘就把數(shù)據(jù)導(dǎo)出了,效率提升了28倍。如果數(shù)據(jù)量更大的話,這個(gè)效率提升會(huì)更明顯。
總結(jié)
UNION ALL 要比UNION快很多,所以,如果可以確認(rèn)合并的兩個(gè)結(jié)果集中不包含重復(fù)數(shù)據(jù)且不需要排序時(shí)的話,那么就使用UNION ALL。
