偽列rownum,偽列就像表中的列一樣,但是在表中并不存儲。偽列只能查詢,不能進行增刪改操作。偽列的數據是由ORACLE進行維護和管理的,最常用的兩個偽列:rownum和rowid。
在查詢的結果集中,ROWNUM為結果集中每一行標識一個行號,第一行返回1,第二行返回2,以此類推。通過ROWNUM偽列可以限制查詢結果集中返回的行數。
ROWNUM與ROWID不同,ROWID是插入記錄時生成,ROWNUM是查詢數據時生成。ROWID標識的是行的物理地址。ROWNUM標識的是查詢結果中的行的次序。
1.ROWNUM:行號
ROWNUM(行號):是在查詢操作時由ORACLE為每一行記錄自動生成的一個編號。
每一次查詢ROWNUM都會重新生成。(查詢的結果中ORACLE給你增加的一個編號,根據結果來重新生成)
ROWNUM永遠按照默認的順序生成。(不受order by的影響)
ROWNUM只能使用 <、 <= ,不能使用 > 、>= 符號,原因是:Oracle是基于行的數據庫,行號永遠是從1開始,即必須有第一行,才有第二行。
1.1.ROWNUM的產生
ROWNUM是有數據庫自己產生的,ROWNUM在查詢的時候自動產生
SELECT ROWNUM,t.*
FROM emp t
1.2.ROWNUM的排序
對數據進行ORDER BY排序,不會影響到ROWNUM的順序。ROWNUM永遠按照默認的順序生成。所謂的“默認的順序”,是指系統按照記錄插入時的順序(其實是ROWID)。
需求:查詢出所有員工信息,按部門號正序排列,并且顯示默認的行號列信息。
SELECT ROWNUM,t.*
FROM emp t
ORDER BY deptno
ORDER BY 原理:將查詢結果(此時行號已經有了,已經和每一行數據綁定了)進行排序。
ORDER BY 是查詢語句出來的結果之后再排序的,ROWNUM是在查詢出來結果的時候產生。所以ORDER BY不會影響到行號.
ORDER BY 排序,不會影響到ROWNUM的順序。ROWNUM永遠按照默認的順序生成。所謂的“默認的順序”,是指系統按照記錄插入時的順序(其實是ROWID)。
1.3.利用ROWNUM行號進行數據分頁(重點)
我們知道,Mysql使用limit關鍵字可以實現分頁,在Mysql中取n條數據可以寫成:
select * from table limit m,n
其中m是指數據中的索引index,n是指從第m+1條開始,取n條。
select * from tablename limit 3,3 //即取出第4條至第6條,3條記錄
但是在ORCALE中并沒有limit關鍵字 那么在ORCALE中如何實現以上需求么?
需求:根據行號查詢出第四條到第六條的員工信息。
錯誤寫法:
SELECT ROWNUM,t. *
FROM emp t
WHERE ROWNUM >=4 AND ROWNUM<=6
以上寫法之所以出錯是因為ROWNUM只能使用 < 、<=,不能使用 > 、>=符號,原因是:Oracle是基于行的數據庫,行號永遠是從1開始,即必須有第一行,才有第二行。
SELECT ROWNUM,t.*
FROM emp t
WHERE ROWNUM<=6; //查詢1-6條記錄
使用子查詢實現根據行號查詢出第四條到第六條的員工信息:
SELECT ROWNUM,t2.
FROM (
SELECT ROWNUM r,t. *
FROM emp t
WHERE ROWNUM <= 6
) t2
WHERE t2.r >= 4
需求:要分頁查詢,每頁3條記錄,查詢第二頁
pageNum=2當前頁碼</br>
pageSize=3 最大記錄數(即每頁顯示幾條記錄)
使用 mysql的分頁查詢語句,需要兩個參數,起始索引和最大記錄數
計算:</br>
起始索引:firstIndex=pageSize*(pageNum-1);</br>
最大記錄數:maxCount=pageSize;
注意:</br>
1.sql中索引是從1開始的</br>
2.兩個參數都是由當前頁碼和最大記錄數計算出來的,所以使用時只定義頁碼和記錄數即可</br>
3.第一頁的參數為(0,3),不是說記錄的起始索引從0開始,這只是一個參數,實際效果是
從第1條記錄開始,記錄數為3條,即查詢1,2,3三條記錄</br>
4.第二頁的參數為(3,3),不是說記錄的起始索引從3開始,這只是一個參數,實際效果是
從第4條記錄開始,記錄數為3條,即查詢4,5,6三條記錄,后面以此類推·····
Mysql語句:
select * from 表名 limit 起始索引,最大記錄數
Oracle分析:</br>
//起始行號
firstRownum = pageSize(pageNum-1)+1</br>
//結束行號
endRownum = firstRownum+pageSize-1</br>
具體計算:
firstRownum=3(2-1)+1=4;
endRownum=4+3-1=6;
寫Oracle的分頁,從子查詢寫起,也就是說從 <= 寫起,或者說從endRownum寫起:
SELECT ROWNUM ,t2.*
FROM (
SELECT ROWNUM r,t.*
FROM emp t
WHERE ROWNUM <= 6
) t2 WHERE t2.r >= 4
優化 --查詢所有字段:
SELECT *
FROM (
SELECT ROWNUM r,t.*
FROM emp t
WHERE ROWNUM <=6
)
優化 -- 結果指定字段:
SELECT empno,ename,job
FROM (
SELECT ROWNUM r,t.*
FROM emp t
WHERE ROWNUM <=6
) WHERE r >=4
需求:按照薪資的高低排序再分頁
SELECT *
FROM (
SELECT ROWNUM r,t.*
FROM emp t
WHERE ROWNUM <=6
ORDER BY sal DESC
)
WHERE r >= 4 ;
以上代碼分頁成功,但定沒有按照薪資的高低排序
改進:先排序薪資,再分頁
SELECT *
FROM (
SELECT ROWNUM r,t.*
FROM (
SELECT *
FROM emp
ORDER BY sal DESC
) t
WHERE ROWNUM <=6
ORDER BY sal DESC
)
WHERE r >=4
Hibernate會自動將所有數據封裝到實體對象(多余出來的行號那一列不會封裝)
如果不需要額外的字段,則只需要指定特定的列名就可以了。
優化:子查詢字段盡量少一些。數據量少。比如,表中有100個字段,但你就想顯示5個,那么,你就子查詢中直接指定5個就ok了。但使用orm框架的建議都查出來。
SELECT * FROM
(
SELECT ROWNUM r,t.* FROM
(SELECT ename,job,sal FROM emp ORDER BY sal DESC) t
WHERE ROWNUM <=6 ORDER BY sal DESC
)
WHERE r >=4
通用查詢代碼
SELECT * FROM
(
SELECT ROWNUM r,t.* FROM
(SELECT ename,job,sal FROM emp ORDER BY sal DESC) t
WHERE ROWNUM <=endRownum ORDER BY sal DESC
)
WHERE r >=firstRownum ;
【提示】:
如何記憶編寫Oracle的分頁?建議寫的時候從里到外來寫,即先寫小于的條件的子查詢(過濾掉rownum大于指定值的數據),再寫大于的條件的查詢(過濾掉rownum小于的值)。
Oracle的分頁中如果需要排序顯示,要先排序操作,再分頁操作。(再嵌套一個子查詢)
性能優化方面:建議在最里層的子查詢中就直接指定字段或者其他的條件,減少數據的處理量。
1.4.ROWID
ROWID(記錄編號):是表的偽列,是用來唯一標識表中的一條記錄,并且間接給出了表行的物理位置,定位表行最快的方式。
主鍵:標識唯一的一條業務數據的標識。主鍵是給業務給用戶用的。不是給數據庫用的。
ROWID:標識唯一的一條數據的。主要是給數據庫用的。類似UUID。
1.4.1. ROWID的查看
SELECT t.*,ROWID FROM emp t;
1.4.2. ROWID的產生
使用insert語句插入數據時,oracle會自動生成rowid 并將其值與表數據一起存放到表行中。
這與rownum有很大不同,rownum不是表中原本的數據,只是在查詢的時候才生成的。rownum默認的排序就是根據rowid.
rowid 是插入數據時自動產生的,即是實際存在的,只是在查詢時才顯示出來,查詢時不寫rowid不顯示但實際存在
1.4.3. ROWID的作用
1)快速刪除重復的記錄的方法
2)根據指定的列刪除包含重復列值的記錄(這種情況一般很少見,因為根據rowid刪除的話很難判斷刪除以后保留下的行的其他列的值到底是什么)
【示例】需求:刪除表中的重復數據,要求保留重復記錄中最早插入的那條。(DBA面試題)
a.準備測試表和測試數據:
create table test (id number, name varchar2(50))
b.插入測試數據
insert into test value(1,'xiaoming')
insert into test value(2,'xiaoming')
insert into test value(3,'xiaoming')
insert into test value(4,'zhongming')
insert into test value(5,'daming')
commit
c.通過rowid,剔除重復xiaoming,保留最早插入的xiaoming
SELECT t.*,ROWID
FROM TEST t
WHERE ROWID > (
SELECT MIN(ROWID)
FROM TEST
);
DELETE FROM TEST t
WHERE ROWID > (
SELECT MIN(ROWID)
FROM TEST
);
d.剔除重復數據
SELECT * FROM TEST WHERE ROWID NOT in(SELECT MIN(ROWID) FROM TEST GROUP BY NAME);
DELETE TEST WHERE ROWID NOT in(SELECT MIN(ROWID) FROM TEST GROUP BY NAME);
注意:刪除重復記錄一定要小心,萬一你的條件有問題,就會刪錯數據.建議刪除之前,可以先用查詢查一下,看是否是目標數據。
數據一旦刪除恢復比較麻煩,但可以恢復,采用日志回滾。一般不要輕易用。