Oracle 那些可能會用到,但又不會寫的語句


在工作中,日常的數據庫開發,其實大部分用到的數據庫知識並不復雜,無非是CRUD【增刪改查】,但是偶爾會有一些特殊的需求,看似合理,但是一時半會兒也想不起來如何下手,所以只能去百度查找。為了方便起見,這里列舉了一些工作中日常用到但又稍微復雜的語句,僅供學習分享使用。如有不足之處,還請指正。

分區排序【partition by】

按指定列分組,同時另一列排序。如:成績表中,按班級分組,成績排序。排出每一班級的成績順序。

分區排序語法:

1 select row_number()[rank(),dense_rank()] OVER (PARTITION BY 分組字段1,分組字段2 ORDER BY 排序字段1) from table;

注意:此處不可以用group by ,因為group by 是分組進行匯總功能。

row_number示例:

1 select sno,cno,degree,
2        row_number()over(partition by cno order by degree desc) mm 
3        from score

 rank示例:

1 SELECT    * 
2 FROM    (select sno,cno,degree,
3           rank()over(partition by cno order by degree desc) mm 
4           from score) 
5 where mm = 1;

rank和row_number的區別

由以上的例子得出,在求第一名成績的時候,不能用row_number(),因為如果同班有兩個並列第一,row_number()只返回一個結果。具體差異如下:

  1. rownumber函數返回一個唯一的值,當碰到相同數據時,排名按照記錄集中記錄的順序依次遞增。 
  2. rank函數返回一個唯一的值,除非遇到相同的數據,此時所有相同數據的排名是一樣的,同時會在最后一條相同記錄和下一條不同記錄的排名之間空出排名。rank()是跳躍排序,有兩個第二名時接下來就是第四名(同樣是在各個分組內)。
  3. dense_rank函數返回一個唯一的值,除非當碰到相同數據,此時所有相同數據的排名都是一樣的。dense_rank()是連續排序,有兩個第二名時仍然跟着第三名。他和row_number的區別在於row_number是沒有重復值的。

遞歸查詢【start with】

如果表中存在層次數據,則可以使用層次化查詢子句查詢出表中行記錄之間的層次關系。如:在一個表中,有兩個字段:id,父id,則遞歸查詢的意思是循環查詢出具有遞歸關系的數據。

語法:

1 [ START WITH CONDITION1 ]
2 CONNECT BY [ NOCYCLE ] CONDITION2
3 [ NOCYCLE ]

start with 子句為可選項,用來標識哪行作為查找樹型結構的第一行(即根節點,可指定多個根節點)。若該子句被省略,則表示所有滿足查詢條件的行作為根節點。2.關於PRIOR PRIOR置於運算符前后的位置,決定着查詢時的檢索順序。 

1. 從根節點自頂向下

1 select empno, mgr, level as lv
2 from scott.emp a
3 start with mgr is null
4 connect by (prior empno) = mgr
5 order by level;

--分析
層次查詢執行邏輯:

  1. 確定上一行(相對於步驟b中的當前行),若start with 子句存在,則以該語句確定的行為上一行,若不存在則將所有的數據行視為上一行。
  2. 從上一行出發,掃描除該行之外所有數據行。
  3. 匹配條件 (prior empno) = mgr

注意:

一元運算符 prior,意思是之前的,指上一行

當前行定義:步驟2中掃描得到的所有行中的某一行

匹配條件含義:當前行字段 mgr 的值等於上一行字段 empno中的值,若滿足則取出該行,並將level + 1,

匹配完所有行記錄后,將滿足條件的行作為上一行,執行步驟 2,3。直到所有行匹配結束。

2. 從根節點自底向上

1 select empno, mgr, level as lv
2 from scott.emp a
3 start with empno = 7876
4 connect by (prior mgr ) = empno
5 order by level;

--分析
層次查詢執行邏輯:

  1. 確定上一行(相對於步驟b中的當前行),若start with 子句存在,則以該語句確定的行為上一行,若不存在則將所有的數據行視為上一行。
  2. 從上一行出發,掃描除該行之外所有數據行。
  3. 匹配條件 (prior mgr ) = empno

注意:

一元運算符 prior,意思是之前的,指上一行。
當前行定義:步驟2中掃描得到的所有行中的某一行。
匹配條件含義:當前行字段 empno 的值等於上一行字段 mgr 中的值,若滿足則取出該行,並將 level + 1,
匹配完所有行記錄后,將滿足條件的行作為上一行,執行步驟2,3。直到所有行匹配結束。

3. 遞歸查詢總結

自頂向下,自下向上口訣:
start with child_id = 10 connect by (prior child_id) = parent_id
prior 和 子列在一起,表示尋找它的子孫,即自頂向下,和父列在一起,表示開始尋找它的爸爸,即自下向上。

一列多行轉換成一行【listagg】

 LISTAGG是Oracle 11g推出的,listagg函數的語法結構如下:

1 LISTAGG( [,]) WITHIN GROUP (ORDER BY ) [OVER (PARTITION BY )]

 listagg雖然是聚合函數,但可以提供分析功能(比如可選的OVER()子句)。使用listagg中,下列中的元素是必須的:

  1. 需要聚合的列或者表達式 
  2. WITH GROUP 關鍵詞 
  3. 分組中的ORDER BY子句

示例:

1 SELECT deptno,LISTAGG(ename, ',') WITHIN GROUP (ORDER BY deptno) AS employees FROM  emp GROUP BY deptno;

拆分字符串成多行【REGEXP_SUBSTR】

有一個問題,需要把一個帶有,的字符串拆分成多行。通過查詢資料,這個操作需要使用以下2個關鍵知識:

  1. REGEXP_SUBSTR函數
  2. 為了實現動態參數,使用 connect by

REGEXP_SUBSTR語法:

1 function REGEXP_SUBSTR(String, pattern, position, occurrence, modifier)

參數說明:

__srcstr :需要進行正則處理的字符串

__pattern :進行匹配的正則表達式

__position :起始位置,從第幾個字符開始正則表達式匹配(默認為1)

__occurrence :標識第幾個匹配組,默認為1

__modifier :模式('i'不區分大小寫進行檢索;'c'區分大小寫進行檢索。默認為'c'。)

示例:

1 select regexp_substr('1,2,3','[^,]+',1,1) result from dual;
2 select regexp_substr('1,2,3','[^,]+',1,2) result from dual;

可以通過connect by可以構造連續的值。如下所示:

1 select rownum from dual connect by rownum<=7;

 

結合REGEXP_SUBSTR 及 connect by 即可實現拆分字符串為多行的需求,最終的語句為: 

1 SELECT REGEXP_SUBSTR ('1,2,3', '[^,]+', 1,rownum)
2 from dual connect by rownum<=LENGTH ('1,2,3') - LENGTH (regexp_replace('1,2,3', ',', ''))+1;

有則修改,無則插入【merge into】

當我們對一個表中數據執行操作:如果存在,進行修改;如果不存在,進行插入。此種情況下,采用merge into 語句最為合適。

merge info語法:

1 MERGE INTO [target-table] A USING [source-table sql] B ON([conditional expression] and [...]...)
2  
3 WHEN MATCHED THEN
4  
5 [UPDATE sql]
6  
7 WHEN NOT MATCHED THEN
8  
9 [INSERT sql]

merge into作用:

判斷B表和A表是否滿足ON中條件,如果滿足則用B表去更新A表,如果不滿足,則將B表數據插入A表但是有很多可選項,如下:

  1. 正常模式
  2. 只update或者只insert
  3. 帶條件的update或帶條件的insert
  4. 全插入insert實現
  5. 帶delete的update(覺得可以用3來實現)

merge into示例:

 1 merge into score a
 2 using (select std_no, c.dept_no
 3          from student c
 4         where c.std_no in
 5               (select std_no from tmp_20210809)) b
 6 on (a.std_no = b.std_no and a.balb_type = '01')
 7 when matched then
 8   update set a.pre_bal = nvl(a.pre_bal, 0) + 5.8
 9 WHEN NOT MATCHED THEN
10   insert
11     (a.bal_id, a.std_no, a.balb_type, a.pre_bal, a.dept_no)
12   values
13     (序列, b.std_no, '01', 5.8, b.dept_no);

 備注

在這個世上,根本就沒有所謂的一蹴而就。只有日積月累的努力,才有厚積薄發的可能。請沉下心來,不要好高騖遠,也不要總是去艷羡別人。專心做好自己的事,當你的才華配得上夢想時,好運自會不期而遇。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM