入職後用了sql也有一年了,對sql和各個資料庫的了解也漸漸多了起來。感覺sql的關聯蠻有意思的,在這裡寫一下自己對這玩意的認識的總結吧。
本篇主要是通過目前接觸到的兩個資料庫–vertica和impala(oracle和mysql也有,但是個人用的不多)的執行計畫來寫的,優化器做到差不多地步的資料庫應該大同小異
p.s.本篇所用的表資料量都在數千萬到數億不等。如果用乙個只有幾十幾百行的小表來跑的話,很可能資料庫的優化器就不再浪費時間做優化了。
又p.s. 兩個資料庫執行計畫相同的話,就只截了一張圖
一、關聯中的on與where
在關聯中這兩個子句都可以起到對資料過濾的作用,但他們過濾的時機不同。on主要是關聯之前過濾,where則是關聯之後過濾。雖然現在大部分資料庫優化器都可以自動把where的過濾條件提前了,但是where在某些時候還是會和on有區別的,同樣的過濾條件放在where或on後面可能會有完全不同的結果。下面就詳細的分析一下。
1、內連線
由於表名不重要,所以此處隱去了表名
語句1
select
t1.*,
t2.*
from
***x t1
inner join
yyyy t2
on t1.city_id = '10303'
and t2.city_id = '10305'
and t1.oid = t2.oid
這條語句在vertica和impala中生成的執行計畫是差不多的,分別是
vertica和impala中的執行計畫(由於他們的執行計畫一樣,lz比較懶就放了一張圖上來):
從執行計畫裡可以看到,他們都是把on中的過濾條件,提前到了掃瞄資料表的時候執行,這樣可以減少需要關聯的資料條目數,提高執行效率
將on中的條件改到where後面,他們的情況都差不多。總之,對於inner join來說,篩選條件放在on或者where後面,效果是差不多的。
2、外連線
2-1 過濾條件放在on後面
語句2-1
select
t1.*,
t2.*
from
***x t1
left join
yyyy t2
on t1.city_id = '10303'
and t2.city_id = '10305'
and t1.oid = t2.oid
vertica和impala中的執行計畫:
從執行計畫中可以看到,對於外連線中的on後面的條件,資料庫只對從表進行了優化,而主表則是先掃瞄全表,到關聯的時候才把on中的條件作為關聯條件執行,這樣的話效率會差很多,之後會給出執行時間對比。
語句2-1的執行時間大約為6.885秒(vertica中的,不得不說vertica效率還是很高的。impala要幾十秒才能完成,這裡偷懶沒有做記錄)
2-2 過濾條件放在where後面
語句2-2
select
t1.*,
t2.*
from
***x t1
left join
yyyy t2
on t1.oid = t2.oid
where
t1.city_id = '10303'
and t2.city_id = '10305'
vertica中的執行計畫:
可以看到在vertica中,這條語句被修改為:先分別對主、從表篩選,然後按照inner join來處理
impala中的執行計畫:
在impala中,也是先對主、從表篩選,但關聯依然按照左連線處理,關聯之後按照other predicates來做篩選。優化器方面果然還是沒有vertica作的更精細。
語句2-2的執行時間大約為3.814秒,比2-1縮短了2倍以上
表面上看起來,2-1和2-2的sql語句的邏輯差不多,只是效率不同,但其實他們達到的效果完全不同。從vertica的執行計畫就可以看出,2-1的關聯依然是按照左連線來處理的,即t1表中關聯失敗的資料依然會出現在結果中,但2-2則被轉成了內聯,即t1表中關聯失敗的資料不會出現在結果中。想象到極端情況:t1按條件篩選出了100條資料,而這100條資料和t2表完全匹配不上,那麼結果是:
這就是這兩條sql語句邏輯上的區別。但2-1的語句的效率實在是太低了(雖然也沒用多久,但主要還是資料量篩的比較少,而且vertica效能還算不錯。如果是幾億的資料相互關聯,那麼效果就會很明顯)。如何用較高的效率來實現2-1的邏輯呢?
有兩個方法,乙個是用子查詢,先對主表用子查詢進行篩選,結果再與從表進行關聯,即:
語句2-3
select
t1.*,
t2.*
from
(select
* from
***x
where
city_id='10303') t1
left join
yyyy t2
ont2.city_id = '10305'
and t1.oid = t2.oid
執行時間約為3.916秒,和2-2的執行速度差不多。
執行計畫很明顯,這裡就不貼了
另外一種方法,則是將對主表的過濾條件放到where後面,對從表的過濾條件放到on後面:
語句2-4
select
t1.*,
t2.*
from
***x t1
left join
yyyy t2
ont2.city_id = '10305'
and t1.oid = t2.oid
where
t1.city_id = '10303'
執行時間大約為4.025秒
vertica和impala中的執行計畫:
可以看到,在執行計畫中,資料庫先分別對主、從表的資料分別作了過濾,然後再執行左連線,用較高的效率實現了語句2-1的效果
總結:
其實寫完後想想,上面的邏輯完全是按照on、where的功能來的,即:
區別就是資料庫的優化器對sql語句進行了優化,把where中的語句提到了最前面執行,這樣可以起到提高語句執行效率的作用。不過不知道為什麼,on中對主表的過濾條件,兩個資料庫的優化器都沒有做優化,不知道oracle中是怎麼處理的,這個放到後面再考慮
另外,從上面的分析也可以看出,把過濾條件放在on還是where後面,不光會影響語句的執行效率,更重要的是,會產生不同的邏輯,在外連線中得到完全不同的結果。因此很有必要對關聯作比較深刻的了解
VS自動優化SQL語句邏輯與效率的疑問
用vs2005時發現乙個問題,生成資料集時,vs會幫你自動優化語句,一般都不懷疑微軟的水平,不過這次優化之後實在是看不懂了,懷疑優化錯了,於是在查詢分析器裡試了一下,結果倒是對的,可是執行計畫裡卻顯示優化後的效率低一些,奇怪了 下面 裡前一行是我寫的,後一行是vs優化出來的 使用了sql serve...
VS自動優化SQL語句邏輯與效率的疑問
用vs2005時發現乙個問題,生成資料集時,vs會幫你自動優化語句,一般都不懷疑微軟的水平,不過這次優化之後實在是看不懂了,懷疑優化錯了,於是在查詢分析器裡試了一下,結果倒是對的,可是執行計畫裡卻顯示優化後的效率低一些,奇怪了 下面 裡前一行是我寫的,後一行是vs優化出來的 使用了sql serve...
SQL中EXISTS與IN的使用及效率
in 和exists 對於以上兩種查詢條件,in是把外表和內錶作hash 連線,而exists 是對外表作loop 迴圈,每次loop 迴圈再對內表進行查詢。一直以來認為exists 比in 效率高的說法是不準確的。在不同的情況下,exists與in的效能各有優缺項,如果查詢的兩個表大小相當,那麼用...