利用索引提示減少分頁的巢狀層數
今天和同事討論了一下索引掃描避免排序的問題,感覺比較有意思,就簡單總結一下。
首先要強調的是,這並不是標準的或者推薦的一種分頁語句的寫法,這種方法需要對錶、索引的結構有清晰的認識。而且這種方法的限制條件很多。因此,這裡只是單獨討論一下,沒用將其放到分頁專題中去。
下面是分頁標準寫法和利用HINT的方式的對比:
SQL> CREATE TABLE T (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30) NOT NULL);
表已建立。
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;
已建立50418行。
SQL> CREATE INDEX IND_T_NAME ON T(NAME);
索引已建立。
SQL> SET AUTOT ON
SQL> SET AUTOT ON EXP
SQL> SELECT *
2 FROM
3 (
4 SELECT A.*, ROWNUM RN
5 FROM
6 (
7 SELECT * FROM T ORDER BY NAME
8 ) A
9 WHERE ROWNUM <= 20
10 ) WHERE RN > 10;
ID NAME RN
---------- ------------------------------ ----------
11501 /1023e902_OraCharsetUTFE 11
11502 /1023e902_OraCharsetUTFE 12
46027 /10240eba_GenPropertySequence 13
46145 /10240eba_GenPropertySequence 14
43203 /1025308f_SunTileScheduler 15
44344 /1025308f_SunTileScheduler 16
37617 /10297c91_SAXAttrList 17
38208 /10297c91_SAXAttrList 18
24613 /103a2e73_DefaultEditorKitEndP 19
24614 /103a2e73_DefaultEditorKitEndP 20
已選擇10行。
執行計劃
----------------------------------------------------------
Plan hash value: 3635692127
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 860 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 20 | 860 | 4 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 45221 | 1324K| 4 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| T | 45221 | 1324K| 4 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | IND_T_NAME | 21 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">10)
2 - filter(ROWNUM<=20)
Note
-----
- dynamic sampling used for this statement
SQL> SELECT *
2 FROM
3 (
4 SELECT /*+ INDEX(T IND_T_NAME) */ T.*, ROWNUM RN
5 FROM T
6 WHERE ROWNUM <= 20
7 )
8 WHERE RN > 10;
ID NAME RN
---------- ------------------------------ ----------
11501 /1023e902_OraCharsetUTFE 11
11502 /1023e902_OraCharsetUTFE 12
46027 /10240eba_GenPropertySequence 13
46145 /10240eba_GenPropertySequence 14
43203 /1025308f_SunTileScheduler 15
44344 /1025308f_SunTileScheduler 16
37617 /10297c91_SAXAttrList 17
38208 /10297c91_SAXAttrList 18
24613 /103a2e73_DefaultEditorKitEndP 19
24614 /103a2e73_DefaultEditorKitEndP 20
已選擇10行。
執行計劃
----------------------------------------------------------
Plan hash value: 2512188149
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 860 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 20 | 860 | 4 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| T | 45221 | 1324K| 4 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | IND_T_NAME | 45221 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">10)
2 - filter(ROWNUM<=20)
Note
-----
- dynamic sampling used for this statement
對於第二種方法,由於Oracle會採用索引全掃描的方式,因此返回的資料本身就是排好序的,避免的ORDER BY語句,而且可以減少一層巢狀。
更重要的是,對於9i版本,很可能標準SQL的寫法不會使用索引,因此第二種寫法的對於分頁查詢前幾頁具有更高的效率。
對於降序的情況,需要改變HINT,由INDEX修改為INDEX_DESC。
上面是這種寫法的優點,不過這種寫法還存在著很多的缺點和不足。
首先,這種寫法要求排序列必須建立索引,且該列不能為空。否則,Oracle不使用INDEX FULL SCAN執行計劃,則無法保證按照正確的排序返回結果。這就造成了SQL的寫法與表結構、列的NOT NULL約束以及索引的情況有關,SQL的書寫不在透明。而且一旦SQL寫法依賴的結構發生了變化,就會導致SQL得到錯誤的結果。
而且這種寫法對於單表訪問有效,對於多個表連線等複雜情況就無法得到正確的結果了。表連線如果採用HASH JOIN,則會導致原有的排序被破壞,只有排序列的表作為驅動表,則連線方式為NESTED LOOP才能保證最終結果的順序。但是,這只是簡單的情況,對於更多更復雜的執行計劃,很難透過HINT的方式來保證最終結果的順序的。
簡單總結一下,這種方法對於偶然一次的查詢是沒有問題的。但是,不能夠替代標準分頁寫到程式中,因為一旦表結構發生了變化,這個SQL就得到錯誤的結果,而且不會有任何錯誤資訊來提示你,問題已經發生了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29067253/viewspace-2141084/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python 利用 for ... else 跳出雙層巢狀迴圈Python巢狀
- 集合框架-集合的巢狀遍歷(多層巢狀)框架巢狀
- 減少該死的 if else 巢狀巢狀
- oracle 巢狀表 索引表 使用Oracle巢狀索引
- 多層巢狀同義詞巢狀
- 使用Await減少回撥巢狀AI巢狀
- iOS多重巢狀頁面iOS巢狀
- 減少程式碼中該死的 if else 巢狀巢狀
- 多層巢狀表型別語法巢狀型別
- C# 利用repeater 的巢狀 實現一個自己想要的 日曆頁面C#巢狀
- 外層函式的變數直接被巢狀函式引用計算函式變數巢狀
- Android實現雙層ViewPager巢狀AndroidViewpager巢狀
- iOS開發·runtime+KVC實現多層字典模型轉換(多層資料:模型巢狀模型,模型巢狀陣列,陣列巢狀模型)iOS模型巢狀陣列
- 仿京東、淘寶首頁,通過兩層巢狀的RecyclerView實現tab的吸頂效果巢狀View
- vue elementUI 表單校驗(多層巢狀)VueUI巢狀
- 多層巢狀後的 Fragment 懶載入實現巢狀Fragment
- Python的if語句多層巢狀怎麼使用Python巢狀
- 不同程式語言在發生stackoverflow之前支援的呼叫棧最大巢狀層數巢狀
- 防止頁面被iframe惡意巢狀巢狀
- jquery div為巢狀,更改了外層,不會改裡層jQuery巢狀
- 多層 UIScrollView 巢狀滾動解決方案UIView巢狀
- 集合的巢狀巢狀
- 盒子的巢狀巢狀
- 漫談 React 元件庫開發(一):多層巢狀彈層元件React元件巢狀
- 集合框架-集合的巢狀遍歷(HashMap巢狀HashMap)框架巢狀HashMap
- 集合框架-集合的巢狀遍歷(HashMap巢狀ArrayList)框架巢狀HashMap
- 集合框架-集合的巢狀遍歷(ArrayList巢狀HashMap)框架巢狀HashMap
- 利用Idea重構功能及Java8語法特性——優化深層巢狀程式碼IdeaJava優化巢狀
- mongodb c driver bson的巢狀訪問與層次結構MongoDB巢狀
- 使用VS2008的頁面巢狀模板巢狀
- iterate的巢狀使用巢狀
- oracle pl/sql儲存過程內外層遊標cursor巢狀引數化示例OracleSQL儲存過程巢狀
- less巢狀巢狀
- Datalist巢狀巢狀
- 微信小程式如何阻止多層巢狀元件的點選事件冒泡微信小程式巢狀元件事件
- 微信小程式自定義tab,多層tab巢狀實現微信小程式巢狀
- c# saf 框架欄位屬性多層巢狀示例C#框架巢狀
- element-ui的----el-form表單校驗巢狀表單校驗(表單多層巢狀)+el-table和el-form巢狀使用表單校驗UIORM巢狀