數據庫優化——SQL語句編寫(DB2)

鑑於CSDN無故刪除博文,本博客不再更新,暫時遷至http://www.db365.net


4 SQL語句編寫(DB2)

本章來自王鵬飛舞動DB2系列《DB2設計與性能優化——原理、方法與實踐》,P242

4.1謂詞

首先要知道,不合理的謂詞會限制優化器對索引和連接方法的選擇。設計謂詞時要注意下面的原則。

(1)保證選擇謂詞足夠簡潔。選擇謂詞要儘量採用簡單的形式,如:列名 = 常數表達式,這樣便於匹配索引。還應該避免使用類型轉換,如果有類型轉換應該顯式地寫出轉換函數,並放在表達式的常數一邊,例如:

cast( colum_char10 as int )= 100

應該寫成:

colum_char10 = cast(100 aschar(10) )

另外,使用參數標記(Parameter Marker)時,要儘量利用Cast函數表明它的類型,以免產生不必要的類型轉換。

同樣,下面的謂詞都應該寫成更優化的形式:

XPRESSN(C) = 'constant'

INTEGER(TRANS_DATE) / 100 =200802

WHERE (CUST_ID * 100) +INT(CUST_CODE) = 123456 ORDER BY 1, 2, 3

它們的正確形式應該爲:

C = INVERXPRESSN( 'constant')

TRANS_DATE BETWEEN 20080201AND 20080229

WHERE CUST_ID = 1234 AND CUST_CODE= '56' ORDER BY 1, 2, 3

(2)使用合理的連接謂詞(Join Predicate)。連接謂詞是選擇連接方法的基本依據。DB2有三種連接方法:嵌套循環、合併連接和散列連接。注意非等式謂詞不能形成合並和散列連接。只有兩個表之間出現至少一個列名相等的等式謂詞時,合併和散列連接纔會被考慮。散列連接更是要求等式兩端的列的長度相等。

爲了使DB2考了儘可能多的連接方法,連接謂詞應該儘量簡單,如列名1 = 列名2。對於等式謂詞要做到等式兩邊的列名長度相等。如果是Varchar,Char類型,它們定義的長度應該相等,如果是Decimal類型,它們的精度應該相同。

(3)不要使用多餘的謂詞。多餘的謂詞不但增加了系統的計算代價,而且會導致中間結果的估算不準確,併產生較差的訪問計劃。例如對於下面的謂詞,(COL_0MONTH =199903)實際上是多餘的,應該去掉。

( COL_0MONTH = 199705

  OR COL_0MONTH = 199805 )

AND NOT

( COL_0MONTH = 199903 )

4.2多餘的連接

有些連接看似合理,但也許是多餘的。連接會涉及對錶的多次掃描,去掉多餘的連接,就會減少不必要的開銷。比如,下面的查詢:

select T1.*

from (T1 join T2 as Q2

        on T1.id = Q2.id and Q2.value = 1 )join T2 as Q3

        on T1.id = Q3.id and Q3.value = 2

去掉不必要的連接後,其形式爲:

select T1.*

from T1, (select id

        from T2

        where value in (1, 2)

        group by id

        having count(*) = 2) as Q1

where T1.id = Q1.id

這樣減少了對T2表的一次連接,大幅度提高了查詢效率。

4.3子查詢

在SQL語言中,當一個查詢語句嵌套在另一個查詢的查詢條件中時,稱爲子查詢。如果一個來自外部查詢的列出現在Where子句的子查詢中,那麼當外部查詢中的列值改變後,子查詢需要重新查詢一次。查詢嵌套的層次越多,效率越低。因此應當儘量避免子查詢。如果子查詢不可避免,那麼首先首先要在子查詢中儘量去掉多餘的行,其次考慮將子查詢轉換成連接。子查詢轉換爲連接後,可以使DB2有機會考慮索引以及更多的連接方法和順序,從而生成更優的計劃。特別是Exist和IN子句,可以方便的轉換成連接。例如,

select *

from T1

where exists(select * fromT2 where T1.c1 = T2.c1)

可以轉換爲,

select distinct T2.c1, T1.*

from T1, T2

where T1.c1 = T2.c1

注意除非T2.c1有唯一性(unique),否則關鍵詞distinct 是必需的,這是爲了保證最終結果和原來的SQL一致。

4.4外連接

要避免不必要的外連接(Outer Join)。Outer Join會限制連接的順序,從而導致一些較好的計劃無法生成。因此要儘可能避免使用Outer Join,無論是Left、Right或者Full Outer Join。

4.5 UNION ALL的使用

在使用Union ALL時,我們要注意UNION ALL之上的謂詞是否被下推。可以查看OptimizedStatement,看看是否每個選擇謂詞都被下推到相應的子查詢中。如:

select *

from (select * from T1

union all

select * from T2) as V1

where c1 between 2000 and2009

在OptimizedStatement中應該看到選擇謂詞c1 between 2000 and 2009被下推到Union All裏面變成:

select *

from (select * from T1 wherec1 between 2000 and 2009

union all

select * from T2 where c1between 2000 and 2009) as V1

如果沒有下推,可能由於選擇謂詞過於複雜,或者DB2的查詢重寫器無法判定下推謂詞是否會提高效率。這時候如果確信下推後會產生較好的計劃,可以手動改變SQL的書寫形式。還可以考慮適當提高優化級別,使原來沒有下推的謂詞被下推。

4.6 Having子句

檢查Having子句中的謂詞是否可以下推。應儘可能把Having子句中的謂詞。一般情況下,根據一定的規則,Having子句如果不含有聚合函數,會經過邏輯優化被下推到Where子句裏面,但也會出現不下推的情況。爲了避免出現沒有下推的情況,在編寫SQL是就應儘量將能下推的謂詞寫在Where子句裏面。

4.7 OFNR和FFNR子句

OFNR(OPTIMIZE FOR NROWS)子句使優化器選擇那些執行時能最快得到前N行結果的訪問計劃,不過查詢仍將返回完整的結果集。FFNR(FETCH FIRST N ROWSONLY)子句顯示查詢結果只需返回N行,這樣減少了返回結果集。根據應用程序的需要使用這兩個子句可以提高SQL語句的性能。

注意DB2不會因爲查詢指定了FETCH FIRST NROWS ONLY而選擇返回前N行結果最快的訪問計劃。所以應該在使用FETCH FIRST NROWS ONLY時,同時使用OPTIMIZE FOR N ROWS。

另外注意,如果應用程序要獲取整個結果集,但卻指定OPTIMIZE FOR NROWS,可能會使性能降低。這是因爲快速返回前N行的訪問計劃並不一定是對於獲取整個結果集最佳的訪問計劃。

4.8使用參數標記

DB2可以通過在動態語句高速緩存中保存訪問計劃和SQL文本,來避免重複編譯一個已編譯過的動態SQL語句。然而,只要謂詞在字面上有一點不同,這個語句與高速緩存中類似的SQL就無法匹配。例如,下面兩個語句在動態語句高速緩存中會被看做不同的語句。

SELECT EMP_ID, EMP_NAME AGEFROM MANAGER_INFO WHERE EMP_ID = 918233

SELECT EMP_ID, EMP_NAME AGE FROMMANAGER_INFO WHERE EMP_ID = 920122

應該考慮把上述語句改成使用參數標記把謂詞常數值傳遞給DB,而不要顯式地在SQL語句中包含它。不過注意,複雜的查詢如果使用參數標記,得到的訪問計劃可能不是最優的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章