MYSQL中in or union的效率問題

今天有人問in一堆條件的sql如何優化。這個很天然就想到用union來代替in來提升效率,網上不少例子也是這麼說的html

http://blog.csdn.net/adparking/article/details/6678911mysql

 http://hi.baidu.com/dereky/blog/item/382c2df536c0532cbc310929.html ios

 http://www.cnblogs.com/xwblog/archive/2012/04/09/2438737.htmlsql

但是我在本機作實驗爲何有相反地結果呢...數據庫

EXPLAIN SELECT * from employees where employees.first_NAME ='Georgi' UNION ALL SELECT * from employees where employees.first_NAME ='Bezalel'

這條語句執行結果481條,執行時間爲0.35s

1 PRIMARY employees ALL 300141 Using wheremysql優化

2 UNION employees ALL 300141 Using whereide

UNION RESULT <union1,2> ALL 性能

explain SELECT * FROM employees WHERE employees.first_name IN ('Georgi','Bezalel')

這條語句的執行結果時間爲0.186s

1 SIMPLE employees ALL 300141 Using where

explain SELECT * FROM employees WHERE employees.first_name ='Georgi' or employees.first_name='Bezalel'

這條語句的執行結果和in的結果差很少

難道是網上的說法有誤?難道和索引有關?在firstname上創建了一個索引

從新執行

union的執行執行計劃以下,執行時間爲0.004s

1 PRIMARY employees ref index_firstname index_firstname 44 const 253 Using where
2 UNION employees ref index_firstname index_firstname 44 const 228 Using where
UNION RESULT <union1,2> ALL

in的執行計劃以下,執行時間也爲0.004s

1 SIMPLE employees range index_firstname index_firstname 44 481 Using where

or的執行計劃以下,執行時間也爲0.004s

1 SIMPLE employees range index_firstname index_firstname 44 481 Using where

感受性能差很少啊。可是注意執行計劃中的type,ref要好於range哦(ref爲非惟一性索引掃描,range爲索引範圍掃描)
忽然感受好像和網上說的差很少了,可是第一個語句走了兩個ref掃描 會不會效率比走一次range的掃描低啊。

要不我再試試主鍵,這個是惟一的,會不會和網上的效果一直呢?

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO=100001 UNION ALL SELECT * FROM employees WHERE employees.EMP_NO=101100
union的執行計劃以下

1 PRIMARY employees const PRIMARY PRIMARY 4 const 1
2 UNION employees const PRIMARY PRIMARY 4 const 1
UNION RESULT <union1,2> ALL

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO IN (100001 ,101100)


in的執行計劃以下

1 SIMPLE employees range PRIMARY PRIMARY 4 2 Using where

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO=100001 OR emp_no=101100


or的執行計劃以下

1 SIMPLE employees range PRIMARY PRIMARY 4 2 Using where

感受結果和第二個實驗仍是差很少。可是const的效率要比range高些。

寫到這裏樓主真的崩潰了,難道是和個人數據庫引擎有關?我用的是大衆的InnoDB啊。。。。

/**
* 20120607 in和exists
*/
這裏我補充一下exist的用法,有人說exists能夠優化in的查詢。exists的用法和in不同,要和子表進行關聯,並且關聯時要用到索引,因此加快了查詢速度。exists做爲where條件時,先對where條件前的主查詢進行查詢而後用主查詢的結果一個一個代入exists的查詢進行判斷,若是爲真則輸出。
網上有種說法,exists的效率並不必定要比in的高,子查詢表大的用exists,子查詢表小的用in。對於not in和not exists,not in 內外表都要進行全表掃描,而not exists的子查詢仍然能夠用索引。因此not exists的查詢要比not in的高。

總結一下,通過一夜的實驗,並無獲得網上實驗的結果,unionall的效率要高於in和or。在字段沒有索引的狀況下,union ALL的效率絕對沒有in和all的高,若是有兩個匹配值,union All 要掃2次全表。若是字段是有索引的,那麼unionall貌似要比in和or看起來高效一些,可是在個人實驗環境下共30W的數據查詢時間沒有多大區別


/**
*20120607 大量數據測試
*/
感謝 fzxu_05的建議。我此次用100個數據來測試,效果更明顯的證實了in的效率要比union高

上語句:
select * from employees where first_name ='Georgi' union
select * from employees where first_name ='Bezalel' union
select * from employees where first_name ='Parto' union
select * from employees where first_name ='Chirstian' union
select * from employees where first_name ='Kyoichi' union
select * from employees where first_name ='Anneke' union
select * from employees where first_name ='Tzvetan' union
select * from employees where first_name ='Saniya' union
select * from employees where first_name ='Sumant' union
select * from employees where first_name ='Duangkaew' union
select * from employees where first_name ='Mary' union
select * from employees where first_name ='Patricio' union
select * from employees where first_name ='Eberhardt' union
select * from employees where first_name ='Berni' union
select * from employees where first_name ='Guoxiang' union
select * from employees where first_name ='Kazuhito' union
select * from employees where first_name ='Cristinel' union
select * from employees where first_name ='Kazuhide' union
select * from employees where first_name ='Lillian' union
select * from employees where first_name ='Mayuko' union
select * from employees where first_name ='Ramzi' union
select * from employees where first_name ='Shahaf' union
select * from employees where first_name ='Bojan' union
select * from employees where first_name ='Suzette' union
select * from employees where first_name ='Prasadram' union
select * from employees where first_name ='Yongqiao' union
select * from employees where first_name ='Divier' union
select * from employees where first_name ='Domenick' union
select * from employees where first_name ='Otmar' union
select * from employees where first_name ='Elvis' union
select * from employees where first_name ='Karsten' union
select * from employees where first_name ='Jeong' union
select * from employees where first_name ='Arif' union
select * from employees where first_name ='Bader' union
select * from employees where first_name ='Alain' union
select * from employees where first_name ='Adamantios' union
select * from employees where first_name ='Pradeep' union
select * from employees where first_name ='Huan' union
select * from employees where first_name ='Alejandro' union
select * from employees where first_name ='Weiyi' union
select * from employees where first_name ='Uri' union
select * from employees where first_name ='Magy' union
select * from employees where first_name ='Yishay' union
select * from employees where first_name ='Mingsen' union
select * from employees where first_name ='Moss' union
select * from employees where first_name ='Lucien' union
select * from employees where first_name ='Zvonko' union
select * from employees where first_name ='Florian' union
select * from employees where first_name ='Basil' union
select * from employees where first_name ='Yinghua' union
select * from employees where first_name ='Hidefumi' union
select * from employees where first_name ='Heping' union
select * from employees where first_name ='Sanjiv' union
select * from employees where first_name ='Mayumi' union
select * from employees where first_name ='Georgy' union
select * from employees where first_name ='Brendon' union
select * from employees where first_name ='Ebbe' union
select * from employees where first_name ='Berhard' union
select * from employees where first_name ='Breannda' union
select * from employees where first_name ='Tse' union
select * from employees where first_name ='Anoosh' union
select * from employees where first_name ='Gino' union
select * from employees where first_name ='Udi' union
select * from employees where first_name ='Satosi' union
select * from employees where first_name ='Kwee' union
select * from employees where first_name ='Claudi' union
select * from employees where first_name ='Charlene' union
select * from employees where first_name ='Margareta' union
select * from employees where first_name ='Reuven' union
select * from employees where first_name ='Hisao' union
select * from employees where first_name ='Hironoby' union
select * from employees where first_name ='Shir' union
select * from employees where first_name ='Mokhtar' union
select * from employees where first_name ='Gao' union
select * from employees where first_name ='Erez' union
select * from employees where first_name ='Mona' union
select * from employees where first_name ='Danel' union
select * from employees where first_name ='Kshitij' union
select * from employees where first_name ='Premal' union
select * from employees where first_name ='Zhongwei' union
select * from employees where first_name ='Parviz' union
select * from employees where first_name ='Vishv' union
select * from employees where first_name ='Tuval' union
select * from employees where first_name ='Kenroku' union
select * from employees where first_name ='Somnath' union
select * from employees where first_name ='Xinglin' union
select * from employees where first_name ='Jungsoon' union
select * from employees where first_name ='Sudharsan' union
select * from employees where first_name ='Kendra' union
select * from employees where first_name ='Amabile' union
select * from employees where first_name ='Valdiodio' union
select * from employees where first_name ='Sailaja' union
select * from employees where first_name ='Arumugam' union
select * from employees where first_name ='Hilari' union
select * from employees where first_name ='Jayson' union
select * from employees where first_name ='Remzi' union
select * from employees where first_name ='Sreekrishna' union
select * from employees where first_name ='Valter' union
select * from employees where first_name ='Hironobu' union
select * from employees where first_name ='Perla'

執行時間17.854s,共23527條數據

下面是in的語句
select * from employees where first_name in ('Georgi', 'Bezalel', 'Parto', 'Chirstian', 'Kyoichi', 'Anneke', 'Tzvetan', 'Saniya', 'Sumant', 
'Duangkaew', 'Mary', 'Patricio', 'Eberhardt', 'Berni', 'Guoxiang', 'Kazuhito', 'Cristinel', 'Kazuhide', 'Lillian', 'Mayuko', 'Ramzi', 'Shahaf',
 'Bojan', 'Suzette', 'Prasadram', 'Yongqiao', 'Divier', 'Domenick', 'Otmar', 'Elvis', 'Karsten', 'Jeong', 'Arif', 'Bader', 'Alain', 'Adamantios',
 'Pradeep', 'Huan', 'Alejandro', 'Weiyi', 'Uri', 'Magy', 'Yishay', 'Mingsen', 'Moss', 'Lucien', 'Zvonko', 'Florian', 'Basil', 'Yinghua', 'Hidefumi', 
'Heping', 'Sanjiv', 'Mayumi', 'Georgy', 'Brendon', 'Ebbe', 'Berhard', 'Breannda', 'Tse', 'Anoosh', 'Gino', 'Udi', 'Satosi', 'Kwee', 'Claudi', 
'Charlene', 'Margareta', 'Reuven', 'Hisao', 'Hironoby', 'Shir', 'Mokhtar', 'Gao', 'Erez', 'Mona', 'Danel', 'Kshitij', 'Premal', 'Zhongwei',
 'Parviz', 'Vishv', 'Tuval', 'Kenroku', 'Somnath', 'Xinglin', 'Jungsoon', 'Sudharsan', 'Kendra', 'Amabile', 'Valdiodio', 'Sailaja', 'Arumugam',
 'Hilari', 'Jayson', 'Remzi', 'Sreekrishna', 'Valter', 'Hironobu', 'Perla')

很明顯,執行時間是0.267s 執行結果23527條。因此union的效果然的很好麼?

/**
* 2012.06.26 
*/
mysql優化器對in子查詢優化時存在一個問題,mysql優化器對於in語句的優化的lazy的,對於in子句,若是不是現實的列表定義,如in('a','b','c'),那麼in子句都會別轉換成exists的相關子查詢(非獨立子查詢,相關子查詢要比獨立子查詢性能低)
對於相關子查詢,有時能夠經過派生表來進行重寫,以免子查詢和外部子查詢的屢次比較操做。
相關文章
相關標籤/搜索