在工作中,日常的數據庫開發,其實大部分用到的數據庫知識並不復雜,無非是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()只返回一個結果。具體差異如下:
- rownumber函數返回一個唯一的值,當碰到相同數據時,排名按照記錄集中記錄的順序依次遞增。
- rank函數返回一個唯一的值,除非遇到相同的數據,此時所有相同數據的排名是一樣的,同時會在最后一條相同記錄和下一條不同記錄的排名之間空出排名。rank()是跳躍排序,有兩個第二名時接下來就是第四名(同樣是在各個分組內)。
- 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;
--分析
層次查詢執行邏輯:
- 確定上一行(相對於步驟b中的當前行),若start with 子句存在,則以該語句確定的行為上一行,若不存在則將所有的數據行視為上一行。
- 從上一行出發,掃描除該行之外所有數據行。
- 匹配條件 (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;
--分析
層次查詢執行邏輯:
- 確定上一行(相對於步驟b中的當前行),若start with 子句存在,則以該語句確定的行為上一行,若不存在則將所有的數據行視為上一行。
- 從上一行出發,掃描除該行之外所有數據行。
- 匹配條件 (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中,下列中的元素是必須的:
- 需要聚合的列或者表達式
- WITH GROUP 關鍵詞
- 分組中的ORDER BY子句
示例:
1 SELECT deptno,LISTAGG(ename, ',') WITHIN GROUP (ORDER BY deptno) AS employees FROM emp GROUP BY deptno;
拆分字符串成多行【REGEXP_SUBSTR】
有一個問題,需要把一個帶有,的字符串拆分成多行。通過查詢資料,這個操作需要使用以下2個關鍵知識:
- REGEXP_SUBSTR函數
- 為了實現動態參數,使用 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表但是有很多可選項,如下:
- 正常模式
- 只update或者只insert
- 帶條件的update或帶條件的insert
- 全插入insert實現
- 帶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);
備注
在這個世上,根本就沒有所謂的一蹴而就。只有日積月累的努力,才有厚積薄發的可能。請沉下心來,不要好高騖遠,也不要總是去艷羡別人。專心做好自己的事,當你的才華配得上夢想時,好運自會不期而遇。