1. 程式人生 > >Oracle ROW_NUMBER() OVER()函式的實際場景使用

Oracle ROW_NUMBER() OVER()函式的實際場景使用

前言

最近開發的系統中有個線上諮詢功能。學生在前臺提交諮詢資訊,教師可以登入後臺回覆諮詢。該功能設計是直接使用一張表,使用是否開始標識該條記錄是否是諮詢的開始,然後使用一個會話id標識是屬於一次諮詢,根據建立時間排序,最後就像聊天一樣。

後來遇到一個需求,就是需要查詢出指定教師回覆的諮詢資訊的第一條問和第一條答。先查詢出所有開始的問,然後使用會話id內連線加子查詢。然而遇到的問題是教師可能有多條回答,如果直接使用mybaits返回一個list,然後再去取第一條不是不可以。但是還是想直接使用sql完成,經過一番查詢找到:

row_number() over(partition by  col1 ORDER BY col2 ASC ) 

(ps:以上問題如果有更好解決辦法的歡迎評論)

資料庫

我將專案的資料庫表進行改造並插入資料

-- ----------------------------
-- Table structure for zxzx
-- ----------------------------
CREATE TABLE "ZXZX" (
"ID" VARCHAR2(64 BYTE) NOT NULL ,
"CONSULT_ID" VARCHAR2(255 BYTE) NULL ,
"CONTENT" VARCHAR2(255 BYTE) NULL ,
"CREATE_DATE" DATE NULL
, "IS_START" VARCHAR2(255 BYTE) NULL , "USER_ID" VARCHAR2(64 BYTE) NULL ) LOGGING NOCOMPRESS NOCACHE ;
-- ---------------------------- -- Records of zxzx -- ---------------------------- INSERT INTO "ZXZX" VALUES ('12sassaasd', '12123123', '第一條問', TO_DATE('2018-03-14 21:09:26', 'YYYY-MM-DD HH24:MI:SS'
), '1', '111');
INSERT INTO "ZXZX" VALUES ('213123qwewq', '12123123', '第一次回答第一條問', TO_DATE('2018-03-14 22:10:16', 'YYYY-MM-DD HH24:MI:SS'), '0', '001'); INSERT INTO "ZXZX" VALUES ('sasdass2342', '12123123', '第二次回答第一條問', TO_DATE('2018-03-14 22:11:07', 'YYYY-MM-DD HH24:MI:SS'), '0', '001'); INSERT INTO "ZXZX" VALUES ('23234wewer', '12123123', '第一條問追問', TO_DATE('2018-03-14 22:22:00', 'YYYY-MM-DD HH24:MI:SS'), '0', '111'); INSERT INTO "ZXZX" VALUES ('345', '12123123', '回答一條追問', TO_DATE('2018-03-14 23:12:55', 'YYYY-MM-DD HH24:MI:SS'), '0', '001'); INSERT INTO "ZXZX" VALUES ('234324', '334455', '第二條問', TO_DATE('2018-03-14 23:08:21', 'YYYY-MM-DD HH24:MI:SS'), '1', '111'); INSERT INTO "ZXZX" VALUES ('5623365', '334455', '回答第二條問', TO_DATE('2018-03-14 23:14:07', 'YYYY-MM-DD HH24:MI:SS'), '0', '001'); INSERT INTO "ZXZX" VALUES ('12314', '112233', '問問問', TO_DATE('2018-03-14 23:14:49', 'YYYY-MM-DD HH24:MI:SS'), '1', '111'); INSERT INTO "ZXZX" VALUES ('12342', '112233', '答答答', TO_DATE('2018-03-14 23:15:15', 'YYYY-MM-DD HH24:MI:SS'), '0', '002'); -- ---------------------------- -- Indexes structure for table zxzx -- ---------------------------- -- ---------------------------- -- Checks structure for table zxzx -- ---------------------------- ALTER TABLE "ZXZX" ADD CHECK ("ID" IS NOT NULL); -- ---------------------------- -- Primary Key structure for table zxzx -- ---------------------------- ALTER TABLE "ZXZX" ADD PRIMARY KEY ("ID");

資料查詢

這裡需要將user_id=001所回答的問題查詢出,也就是上圖紅框中的資訊。

  • 首先查詢001的回答,這裡就可能每個會話有多條,使用前面提到的row_number() over()便可以解決重複問題:
SELECT
       row_number()  over(partition by  consult_id  ORDER BY create_date ASC ) as rn,
       id,      
       consult_id ,      
       content,      
       user_id ,      
       is_start,      
       create_date      
FROM zxzx  
WHERE user_id = '001' AND is_start !='1'

  • 接著上面的查詢,可以看出排序分組之後的資料都有一個rn編號,接著將rn為一取出即可:

  • 然後需要查詢出所有的問的記錄。將上面作為子查詢,使用內連線並用consult_id欄位連線。
select
   w.id,      
   w.consult_id ,      
   w.content,      
   w.user_id ,      
   w.is_start,      
   w.create_date, 
   h.id,      
   h.consult_id ,      
   h.content,      
   h.user_id ,      
   h.is_start,      
   h.create_date
from zxzx w inner join (
  select * from(
    SELECT
           row_number()  over(partition by  consult_id  ORDER BY create_date ASC ) as rn,
           id,      
           consult_id ,      
           content,      
           user_id ,      
           is_start,      
           create_date      
    FROM zxzx  
    WHERE user_id = '001' AND is_start !='1'
  )where rn = 1
) h on h.consult_id = w.consult_id 
where w.is_start = '1'
  • 最後查詢結果

知識點

資料庫結果去重

上面說到使用row_number() over()函式獲取根據某個欄位分組後的一條資料,這就有點像“去重”。在Oracle中還可以使用distinct來返回唯一值。

  1. distinct關鍵字,語法如下:
SELECT DISTINCT col FROM table_name

​ 在使用關鍵字 distinct 的時候,主要是要知道其作用於單個欄位和多個欄位的時候是有區別的:

  • 作用於單個欄位時,其“去重”的是表中所有該欄位值重複的資料;
  • 作用於多個欄位的時候,其“去重”的表中所有欄位(即 distinct 具體作用的多個欄位)值都相同的資料。

    1. row_number() over() 函式,語法如下:
row_number() over(partition by  col1 ORDER BY col2 ASC ) 

​ row_number()函式用於給記錄進行標號,over()函式的作用是將表中的記錄進行分組和排序。語法類似上面:將表中的資料按照col1進行分組,按照col2排序。partition by:表示分組。該函式主要是根據分組進行排序之後然後取出第一條資料以實現“去重”

連線查詢

  1. 內連線inner join on:只顯示兩張表都匹配的結果;
  2. 左外連線left join on:以左邊的表為基準,顯示出左表的全部欄位和右表所匹配的欄位,空的用null表示;
  3. 右外連線right join on:與左外連線相反,右外連線會查詢出右表的所有欄位,顯示左表與右表匹配的欄位,空用null表示;
  4. 全連線full join on:顯示兩張表全部內容。