SQL表關聯中的邏輯與效率

2021-07-02 14:44:21 字數 3704 閱讀 9241

入職後用了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的效能各有優缺項,如果查詢的兩個表大小相當,那麼用...