Oracle的偽列

偽列rownum,偽列就像表中的列一樣,但是在表中并不存儲。偽列只能查詢,不能進行增刪改操作。偽列的數(shù)據(jù)是由ORACLE進行維護和管理的,最常用的兩個偽列:rownum和rowid。

在查詢的結(jié)果集中,ROWNUM為結(jié)果集中每一行標(biāo)識一個行號,第一行返回1,第二行返回2,以此類推。通過ROWNUM偽列可以限制查詢結(jié)果集中返回的行數(shù)。

ROWNUM與ROWID不同,ROWID是插入記錄時生成,ROWNUM是查詢數(shù)據(jù)時生成。ROWID標(biāo)識的是行的物理地址。ROWNUM標(biāo)識的是查詢結(jié)果中的行的次序。

1.ROWNUM:行號

ROWNUM(行號):是在查詢操作時由ORACLE為每一行記錄自動生成的一個編號。

每一次查詢ROWNUM都會重新生成。(查詢的結(jié)果中ORACLE給你增加的一個編號,根據(jù)結(jié)果來重新生成)

ROWNUM永遠按照默認的順序生成。(不受order by的影響)

ROWNUM只能使用 <、 <= ,不能使用 > 、>= 符號,原因是:Oracle是基于行的數(shù)據(jù)庫,行號永遠是從1開始,即必須有第一行,才有第二行。

1.1.ROWNUM的產(chǎn)生

ROWNUM是有數(shù)據(jù)庫自己產(chǎn)生的,ROWNUM在查詢的時候自動產(chǎn)生

SELECT ROWNUM,t.*
FROM emp t

1.2.ROWNUM的排序

對數(shù)據(jù)進行ORDER BY排序,不會影響到ROWNUM的順序。ROWNUM永遠按照默認的順序生成。所謂的“默認的順序”,是指系統(tǒng)按照記錄插入時的順序(其實是ROWID)。

需求:查詢出所有員工信息,按部門號正序排列,并且顯示默認的行號列信息。

SELECT ROWNUM,t.*
FROM emp t
ORDER BY deptno

ORDER BY 原理:將查詢結(jié)果(此時行號已經(jīng)有了,已經(jīng)和每一行數(shù)據(jù)綁定了)進行排序。

ORDER BY 是查詢語句出來的結(jié)果之后再排序的,ROWNUM是在查詢出來結(jié)果的時候產(chǎn)生。所以O(shè)RDER BY不會影響到行號.

ORDER BY 排序,不會影響到ROWNUM的順序。ROWNUM永遠按照默認的順序生成。所謂的“默認的順序”,是指系統(tǒng)按照記錄插入時的順序(其實是ROWID)。

1.3.利用ROWNUM行號進行數(shù)據(jù)分頁(重點)

我們知道,Mysql使用limit關(guān)鍵字可以實現(xiàn)分頁,在Mysql中取n條數(shù)據(jù)可以寫成:

select * from table limit m,n

其中m是指數(shù)據(jù)中的索引index,n是指從第m+1條開始,取n條。

select * from tablename limit 3,3 //即取出第4條至第6條,3條記錄

但是在ORCALE中并沒有l(wèi)imit關(guān)鍵字 那么在ORCALE中如何實現(xiàn)以上需求么?

需求:根據(jù)行號查詢出第四條到第六條的員工信息。

錯誤寫法:

SELECT ROWNUM,t. *
FROM emp t
WHERE ROWNUM >=4 AND ROWNUM<=6

以上寫法之所以出錯是因為ROWNUM只能使用 < 、<=,不能使用 > 、>=符號,原因是:Oracle是基于行的數(shù)據(jù)庫,行號永遠是從1開始,即必須有第一行,才有第二行。

SELECT ROWNUM,t.* 
FROM emp t 
WHERE ROWNUM<=6; //查詢1-6條記錄

使用子查詢實現(xiàn)根據(jù)行號查詢出第四條到第六條的員工信息:

SELECT ROWNUM,t2. 
FROM (
    SELECT ROWNUM r,t. *
    FROM emp t 
    WHERE ROWNUM <= 6
)  t2
WHERE t2.r >= 4

需求:要分頁查詢,每頁3條記錄,查詢第二頁

pageNum=2當(dāng)前頁碼</br>
pageSize=3 最大記錄數(shù)(即每頁顯示幾條記錄)

使用 mysql的分頁查詢語句,需要兩個參數(shù),起始索引和最大記錄數(shù)

計算:</br>
起始索引:firstIndex=pageSize*(pageNum-1);</br>
最大記錄數(shù):maxCount=pageSize;

注意:</br>
1.sql中索引是從1開始的</br>
2.兩個參數(shù)都是由當(dāng)前頁碼和最大記錄數(shù)計算出來的,所以使用時只定義頁碼和記錄數(shù)即可</br>
3.第一頁的參數(shù)為(0,3),不是說記錄的起始索引從0開始,這只是一個參數(shù),實際效果是
從第1條記錄開始,記錄數(shù)為3條,即查詢1,2,3三條記錄</br>
4.第二頁的參數(shù)為(3,3),不是說記錄的起始索引從3開始,這只是一個參數(shù),實際效果是
從第4條記錄開始,記錄數(shù)為3條,即查詢4,5,6三條記錄,后面以此類推·····

Mysql語句:
select * from 表名 limit 起始索引,最大記錄數(shù)

Oracle分析:</br>
//起始行號
firstRownum = pageSize(pageNum-1)+1</br>
//結(jié)束行號
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

優(yōu)化 --查詢所有字段:

SELECT * 
FROM (
    SELECT ROWNUM r,t.* 
    FROM emp t 
    WHERE ROWNUM <=6
)

優(yōu)化 -- 結(jié)果指定字段:

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會自動將所有數(shù)據(jù)封裝到實體對象(多余出來的行號那一列不會封裝)

如果不需要額外的字段,則只需要指定特定的列名就可以了。

優(yōu)化:子查詢字段盡量少一些。數(shù)據(jù)量少。比如,表中有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大于指定值的數(shù)據(jù)),再寫大于的條件的查詢(過濾掉rownum小于的值)。

Oracle的分頁中如果需要排序顯示,要先排序操作,再分頁操作。(再嵌套一個子查詢)

性能優(yōu)化方面:建議在最里層的子查詢中就直接指定字段或者其他的條件,減少數(shù)據(jù)的處理量。

1.4.ROWID

ROWID(記錄編號):是表的偽列,是用來唯一標(biāo)識表中的一條記錄,并且間接給出了表行的物理位置,定位表行最快的方式。

主鍵:標(biāo)識唯一的一條業(yè)務(wù)數(shù)據(jù)的標(biāo)識。主鍵是給業(yè)務(wù)給用戶用的。不是給數(shù)據(jù)庫用的。

ROWID:標(biāo)識唯一的一條數(shù)據(jù)的。主要是給數(shù)據(jù)庫用的。類似UUID。

1.4.1. ROWID的查看

SELECT t.*,ROWID FROM emp t;

1.4.2. ROWID的產(chǎn)生

使用insert語句插入數(shù)據(jù)時,oracle會自動生成rowid 并將其值與表數(shù)據(jù)一起存放到表行中。

這與rownum有很大不同,rownum不是表中原本的數(shù)據(jù),只是在查詢的時候才生成的。rownum默認的排序就是根據(jù)rowid.

rowid 是插入數(shù)據(jù)時自動產(chǎn)生的,即是實際存在的,只是在查詢時才顯示出來,查詢時不寫rowid不顯示但實際存在

1.4.3. ROWID的作用

1)快速刪除重復(fù)的記錄的方法

2)根據(jù)指定的列刪除包含重復(fù)列值的記錄(這種情況一般很少見,因為根據(jù)rowid刪除的話很難判斷刪除以后保留下的行的其他列的值到底是什么)

【示例】需求:刪除表中的重復(fù)數(shù)據(jù),要求保留重復(fù)記錄中最早插入的那條。(DBA面試題)

a.準(zhǔn)備測試表和測試數(shù)據(jù):

create table  test (id number, name varchar2(50))

b.插入測試數(shù)據(jù)

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,剔除重復(fù)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.剔除重復(fù)數(shù)據(jù)

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);

注意:刪除重復(fù)記錄一定要小心,萬一你的條件有問題,就會刪錯數(shù)據(jù).建議刪除之前,可以先用查詢查一下,看是否是目標(biāo)數(shù)據(jù)。

數(shù)據(jù)一旦刪除恢復(fù)比較麻煩,但可以恢復(fù),采用日志回滾。一般不要輕易用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容