1. 程式人生 > >SUM與GROUP BY語句的優化

SUM與GROUP BY語句的優化

ble 排序。 Inventor 好的 海量 超過 ash 無需 原因

一.SUM與GROUP BY語句的優化:

1.原語句為:
SELECT IID.INVENTORY_ITEM_ID, SUM(IID.AVAILABLE_TO_PROMISE_DIFF), SUM(IID.QUANTITY_ON_HAND_DIFF), SUM(IID.ACCOUNTING_QUANTITY_DIFF)
FROM BOSENT.INVENTORY_ITEM_DETAIL IID
WHERE ((IID.INVENTORY_ITEM_ID = ‘?‘))
GROUP BY IID.INVENTORY_ITEM_ID

SQL語句中使用GROUP BY的原因是select子句中有INVENTORY_ITEM_ID這個字段。

分析上面的SQL語句,INVENTORY_ITEM_ID作為WHERE子句中的條件,它的值是已知的,無需從SQL中查詢出來,所以可以把這個字段從select子句中去掉,同進去除GROUP BY子句。SQL可改寫為:
SELECT SUM(IID.AVAILABLE_TO_PROMISE_DIFF), SUM(IID.QUANTITY_ON_HAND_DIFF), SUM(IID.ACCOUNTING_QUANTITY_DIFF)
FROM BOSENT.INVENTORY_ITEM_DETAIL IID
WHERE ((IID.INVENTORY_ITEM_ID = ‘?‘))

2.執行計劃
(1)優化前SQL語句的執行計劃 (使用160001代替?的值,目的是看執行計劃)

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 1 | 19 | 8008 (1)| 00:01:37 |
| 1 | SORT GROUP BY NOSORT| | 1 | 19 | 8008 (1)| 00:01:37 |
|* 2 | TABLE ACCESS FULL | INVENTORY_ITEM_DETAIL | 670K| 12M| 8008 (1)| 00:01:37 |

Predicate Information (identified by operation id):

2 - filter("IID"."INVENTORY_ITEM_ID"=‘160001‘)

(2)優化前SQL語句的執行計劃

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 1 | 19 | 8008 (1)| 00:01:37 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS FULL| INVENTORY_ITEM_DETAIL | 670K| 12M| 8008 (1)| 00:01:37 |

Predicate Information (identified by operation id):

2 - filter("IID"."INVENTORY_ITEM_ID"=‘160001‘)

2.執行計劃分析
(1) SORT(AGGREGATE)
SORT(AGGREGATE)是指在沒有GROUP BY的前提下,使用統計函數對全部數據對象進行運算時所顯示出來的執行計劃。在使用SUM、COUNT、MIN、MAX、AVG等統計函數時並不執行一般排序。而是在讀取表中數據的同時以每一行數據為對象進行求和,AVG或者COUNT也同樣是以所讀取的每一行數據為對象進行反復計算的。
SORT AGGREGATE做為sort的option之一比較特殊,它並不做sort,SORT AGGREGATE作用於所有的data set上。

那麽MIN和MAX是如何進行計算的呢?其方法與上面所介紹的基本相同。MIN的執行方法為在最開始將所讀取的第一個值記錄下來,然後將該值與下一個讀取的值進行比較,如果比該值小則將其替換。在執行該統計操作時如果可以使用索引,則能夠獲得非常好的效果。在該執行計劃中雖然顯示的是索引全掃描,但實際上,僅僅會讀取第一個索引塊,之後並不繼續進行掃描(因為索引是排序的,因此索引列值的最小值必然在索引的第一個塊上)。而MAX則是僅僅讀取最後一個索引塊。想要獲得這樣的執行計劃,不能使用WHERE和GROUP BY,當然還要求所要進行統計的列必須是索引的先行列(如果是組合索引,則該列必須位於最前面),並且在SELECT-List中不能添加其他任何額外的操作要求。

(2) SORT(GROUP BY)
該操作是將數據行向不同分組中聚集的操作,即依據查詢語句中所使用的GROUP BY而進行的相關操作,為了進行分組就只能進行排序,因此所需分組的數據量越大則代價就越高。

如果想通過GROUP BY 將海量表分為上千個組,在這種對超大型表執行GROUP BY時,就可以明顯地感覺到該操作所需要的代價。當所要排序的數據超過一定量時,其代價就會變得非常大,解決該問題的一個方法就是使用HASH(GROUP BY)。

(3) SORT(UNIQUE)
該操作是指把查詢語句的輸出結果變成唯一集合的過程。出現該排序的情況有兩種,一種是使用了“DISTINCT”,另外一種是子查詢以提供者角色向主查詢提供其執行結果。

SELECT order_id,order_date
FROM orders
WHERE order_id in (SELECT order_id
FROM order_item
WHERE item_id = :b1
AND order_qty>100);
由於主查詢的結果必須存在於子查詢中,在這裏必須要將作為“M”集合的子查詢轉換為不允許重復元素存在的“1”集合,所以執行了SORT(UNIQUE)操作。如果該子查詢被放在主查詢之後執行,則該排序執行計劃就不會被顯示出來,此時顯示的是FILTER。

3.另一種不修改程序和SQL語句的優化方法

表INVENTORY_ITEM_DETAIL已有INVENTORY_ITEM_ID單個字段的索引,這個表共有26個字段,SUM只對其中三個字段進行統計,通過創建下面的一個復合索引,性能有大幅提高。
CREATE INDEX
I_INVENTORY_ITEM_DETAIL_XX
ON
INVENTORY_ITEM_DETAIL
(
INVENTORY_ITEM_ID,AVAILABLE_TO_PROMISE_DIFF,QUANTITY_ON_HAND_DIFF,ACCOUNTING_QUANTITY_DIFF
);

經驗證測試,創建這個索引後,取款交易響應時間從4.5s下降到2.0s;存款交易從2.7s下降到1.6s。效果明顯。(壓力測試環境中表INVENTORY_ITEM_DETAIL的記錄數是213萬條記錄。測試機器是虛擬機)

4.把第2,3種優化方法同時用上,性能更好。


SUM與GROUP BY語句的優化