提一個問題: oracle在order by 排序時,是穩定排序算法嗎? 發現用一個type進行排序後,作分頁查詢,第一頁的數據和第二頁的數據有重複 懷疑是order by 時,兩次排列的順序不一致 php
看到業務描述的問題能夠獲得的結論order by排序不穩定,還有第一個印象就是,type確定是不惟一的,而且沒有索引吧。 html
這裏先科普下排序的穩定性,舉個最簡單的例子,1,2,3,1,4,5 排序 排序的結果是1,1,2,3,4,5,這時候觀察這個1,若是第一個1仍是排序前的那個1,那麼算法是穩定的。也就是說相等數在排序後不發生交換。 mysql
還記得之前數據結構中的幾種排序算法: 算法
選擇排序複雜度爲n*n,不穩定排序, sql
快速排序複雜度爲n*n,不穩定排序, shell
希爾排序複雜度爲nlogn,不穩定排序, 數據結構
堆排序複雜度爲nlogn,不穩定排序, oracle
冒泡排序複雜度爲n*n,穩定排序, 測試
插入排序複雜度爲n*n,穩定排序, ui
歸併排序複雜度爲nlogn,穩定排序
基數排序的複雜度和位數是有關的,是穩定排序。
好了回到正題,本機測試,插入幾條測試數據,表結構就兩個字段,id和name,沒有索引
SELECT ROWNUM,ZZ_TEST.* FROM ZZ_TEST;
1 2 test 2 2 test 3 3 test 4 4 test 5 1 test能夠看到,默認差的時候是是按照rownum排序的。
而後按照name排序,
SELECT ROWNUM,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name"
1 2 test 2 2 test 5 1 test 4 4 test 3 3 test能夠看到,排列的順序不是按照rownum來排序了。
這裏再插入一個知識,如何在oracle裏查看執行計劃,我敲了半天的explain 發現沒有用。。。
原來是這麼看的,並且消息要比mysql詳細多了。:
Explain plan for SELECT ROWNUM,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name" select * from table(dbms_xplan.display());
------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 16 (7)| 00:00:01 | | 1 | SORT ORDER BY | | 1 | 8 | 16 (7)| 00:00:01 | | 2 | COUNT | | | | | | | 3 | TABLE ACCESS FULL| ZZ_TEST | 1 | 8 | 15 (0)| 00:00:01 | -------------------------------------------------------------------------------好了,那麼排序和索引有沒有關係呢?
咱們先在type上面加一個索引試試,這裏我清空了從新插入了5個數據
SELECT ROWNUM,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name"
1 3 test 2 4 test 5 2 test 4 1 test 3 5 test貌似不給力啊老溼。
好,刪掉type的索引,在id上加索引,清空表再插入5個數據
SELECT ROWNUM,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name"
1 3 test 2 4 test 5 2 test 4 1 test 3 5 test
好吧。原來帶上索引都不給力啊。。。
可是不對啊。。。總感受不對勁啊。沒錯。。。我TMD一直再用的rownum而不是rowID啊。我必定是最近寫分頁寫多了,坑爹啊。
這裏簡單的分辨一下rownum和rowid的區別,rownum是返回結果集的一個僞數列,用來標記返回結果的順序,而rowid是一個物理值用來標記存儲位置的。這個值是惟一而固定的
rowid和rownum都是虛列,但含義徹底不一樣。rowid是物理地址,用於定位oracle中具體數據的物理存儲位置,而rownum則是sql的輸出結果排序。通俗的講:rowid是相對不變的,rownum會變化,尤爲是使用order by的時候。那麼咱們再查下rowid試試,這時候表沒有索引
SELECT rowid as rono,ROWNUM,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name"
AAA7JjAB9AAAD+RAAA 1 3 test AAA7JjAB9AAAD+RAAB 2 4 test AAA7JjAB9AAAD+RAAG 5 2 test AAA7JjAB9AAAD+RAAD 4 1 test AAA7JjAB9AAAD+RAAC 3 5 test
感受rowno和rowid一個樣子啊
清空表,再在name上創建一個索引,而後在插入5條數據
AAA7JjAB9AAAD+RAAA 1 3 test AAA7JjAB9AAAD+RAAB 2 4 test AAA7JjAB9AAAD+RAAG 5 2 test AAA7JjAB9AAAD+RAAD 4 1 test AAA7JjAB9AAAD+RAAC 3 5 test因此,也不是rowid的問題,oralce的排序就是不穩定的。
這裏有個小技巧,由於rownum的輸出順序並非排序的結果 那麼如何能輸出排序順序的rownum呢?可使用嵌套查詢,這個和分頁寫法是一個道理的
select ROWNUM ,t.* from (SELECT rowid rono,ZZ_TEST.* FROM ZZ_TEST ORDER BY ZZ_TEST."name") t
這裏再插入一個小知識,如何在oracle下看錶的
select * from user_tables 能夠查詢出全部的用戶表
select table_name from user_tables;查看其它信息能夠參考這個博客:
http://jerryhui.blog.51cto.com/109787/241127
這裏有一個博客的實驗結果是很符合預期的,可是我是真的沒有作出這個結果:
http://blog.csdn.net/gushangzao/article/details/7251138
http://www.itpub.net/forum.php?mod=viewthread&tid=1339419
/**
20121102 oracle的表類型
*/
http://www.2cto.com/database/201110/109559.html
原來oracle也分這麼多表類型,感受和mysql的不一樣的引擎差很少的。看這個介紹好像oracle的數據存儲確實和表類型有關,可是我找了半天不資料仍是不知道怎麼查看和更改表類型。求些靠譜資料。。。
總結一下,oralce的排序是不穩定的排序,對於相同字段的排序輸出結果貌似和索引和rowid都沒有規律可循,和網上的某些結論不太一致,這也是我今天比較困惑的地方。另外熟悉幾種排序的穩定性,會查看oracle的執行計劃,oracle如何看錶的名及表結構,知道rownum和rowid的區別,知道經過嵌套查詢輸出有規律的rownum