1.情景展示
根據第一個游標的數據產生第二個游標的數據,如何實現遍歷?
三級關聯查詢如何實現?
存儲過程如何使用臨時表返回游標數據?
表結構展示


2.創建臨時表
方式一:直接創建
/**
* 創建臨時指標表
*/
CREATE GLOBAL TEMPORARY TABLE TAB_INDEX_TEMP(A_ID NUMBER,--一級指標ID
A_INDEXNAME VARCHAR2(50), --一級指標名稱
A_TOTALWEIGHT NUMBER, --權重占比
B_ID NUMBER, --二級指標ID
B_INDEXNAME VARCHAR2(200), ----二級指標名稱
B_INDEXSCORE NUMBER, --指標分值
B_COUNTER VARCHAR2(200), --計算方法
B_REMARK VARCHAR2(200),--備注
B_ISTOTAL VARCHAR2(2),--是否合計
B_ISCHILD VARCHAR2(2))ON COMMIT DELETE ROWS;/*事務提交時,清空臨時表數據*/
方式二:動態創建
DECLARE
/* TABLE計數,用於判斷臨時表是否存在*/
V_TAB_COUNT NUMBER(1);
/* 用於創建臨時表SQL */
V_TAB_SQL VARCHAR2(1000);
BEGIN
-- 1.創建臨時表
SELECT COUNT(1)
INTO V_TAB_COUNT
FROM ALL_TABLES
WHERE TABLE_NAME = 'TAB_INDEX_TEMP';
--不存在就創建
IF V_TAB_COUNT = 0 THEN
V_TAB_SQL := 'CREATE GLOBAL TEMPORARY TABLE TAB_INDEX_TEMP(A_ID NUMBER,
A_INDEXNAME VARCHAR2(50),
A_TOTALWEIGHT NUMBER,
B_ID NUMBER,
B_INDEXNAME VARCHAR2(200),
B_INDEXSCORE NUMBER,
B_COUNTER VARCHAR2(200),
B_REMARK VARCHAR2(200),
B_ISTOTAL VARCHAR2(2),
B_ISCHILD VARCHAR2(2))';
V_TAB_SQL := V_TAB_SQL || ' ON COMMIT delete ROWS'; /*事務提交時,清空臨時表數據*/
EXECUTE IMMEDIATE V_TAB_SQL; --執行SQL
END IF;
END;
說明:臨時表類型采用事務類型,至於好處見文章底部鏈接。
也可以使用會話型臨時表,只不過需要在每次插入前先清空表數據。
3.解決方案
三級關聯查詢思路:先查一級指標,根據一級查二級,判斷二級查三級
CREATE OR REPLACE PROCEDURE PRC_INDEX_SEARCH(OUT_CURSOR OUT SYS_REFCURSOR) IS
/**
* 內容:三級指標關聯查詢
* 日期:2020/04/21
* 作者:MARYDON
* 流程:一級指標-->二級指標(合計除外)-->三級指標-->二級指標(只有合計)
*/
/*查詢一級指標(一級指標下如果沒有二級指標的話,是不會插入到臨時表的)*/
CURSOR CUR_FIRST_INDEX IS
SELECT A.ID A_ID, --一級指標ID
A.INDEXNAME A_INDEXNAME --一級指標名稱
FROM INDEX_A A
WHERE A.STATUS = 1 --一級指標處於啟用狀態
ORDER BY A_ID;
/*定義游標變量,該變量的類型為基於游標CUR_FIRST_INDEX的行記錄*/
ROW_CUR_FIRST_INDEX CUR_FIRST_INDEX%ROWTYPE;
/*一級指標ID*/
V_A_ID INDEX_A.ID%TYPE;
/*查詢一級指標對應的二級指標
*動態游標數據
*/
CURSOR CUR_SECOND_INDEX IS
SELECT A.ID A_ID, --一級指標ID
A.INDEXNAME A_INDEXNAME, --一級指標名稱
A.TOTALWEIGHT A_TOTALWEIGHT, --權重占比
B.ID B_ID, --二級指標ID
B.INDEXNAME B_INDEXNAME, ----二級指標名稱
B.INDEXSCORE B_INDEXSCORE, --指標分值
B.COUNTER B_COUNTER, --計算方法
B.REMARK B_REMARK, --備注
B.ISTOTAL B_ISTOTAL, --是否合計
B.ISCHILD B_ISCHILD --是否有子指標
FROM INDEX_B B, INDEX_A A
WHERE B.STATUS = 1 --二級指標處於啟用狀態(不用擔心一級指標是否禁用問題)
AND B.INDEXTYPE = 2 --二級指標
AND B.ISTOTAL = 2 --非合計
AND B.INDEXA_ID = V_A_ID/*必須用變量*/
AND B.INDEXA_ID = A.ID --兩表關聯
ORDER BY B_ID;
/*定義游標變量,該變量的類型為基於游標CUR_SECOND_INDEX的行記錄*/
ROW_CUR_SECOND_INDEX CUR_SECOND_INDEX%ROWTYPE;
/*定義子指標變量*/
V_ISCHILD INDEX_B.ISCHILD%TYPE;
BEGIN
-- 1.創建臨時表
--2.遍歷雙重游標(一級指標)
/*2.1遍歷第一層游標*/
FOR ROW_CUR_FIRST_INDEX IN CUR_FIRST_INDEX LOOP
--2.1.1一級指標ID 賦值(第二重游標數據的關鍵,V_A_ID變化,CUR_SECOND_INDEX數據也會隨之改變)
V_A_ID := ROW_CUR_FIRST_INDEX.A_ID;
/*2.2一級指標對應的二級指標*/
FOR ROW_CUR_SECOND_INDEX IN CUR_SECOND_INDEX LOOP
--2.2.1將二重游標行數據插入臨時表
INSERT INTO TAB_INDEX_TEMP
VALUES
(ROW_CUR_SECOND_INDEX.A_ID,
ROW_CUR_SECOND_INDEX.A_INDEXNAME,
ROW_CUR_SECOND_INDEX.A_TOTALWEIGHT,
ROW_CUR_SECOND_INDEX.B_ID,
ROW_CUR_SECOND_INDEX.B_INDEXNAME,
ROW_CUR_SECOND_INDEX.B_INDEXSCORE,
ROW_CUR_SECOND_INDEX.B_COUNTER,
ROW_CUR_SECOND_INDEX.B_REMARK,
ROW_CUR_SECOND_INDEX.B_ISTOTAL,
ROW_CUR_SECOND_INDEX.B_ISCHILD);
--2.2.2指標分值 賦值
V_ISCHILD := ROW_CUR_SECOND_INDEX.B_ISCHILD;
--2.2.3插入三級指標(1說明有子指標)
IF V_ISCHILD = 1 THEN
INSERT INTO TAB_INDEX_TEMP
SELECT A.ID,
A.INDEXNAME,
A.TOTALWEIGHT,
B.ID B_ID,
B.INDEXNAME,
B.INDEXSCORE,
B.COUNTER,
B.REMARK,
B.ISTOTAL,
B.ISCHILD
FROM INDEX_B B, INDEX_A A
WHERE B.STATUS = 1 --三級指標啟用
AND B.INDEXB_ID = ROW_CUR_SECOND_INDEX.B_ID --三級指標的父級ID
AND B.INDEXA_ID = A.ID --兩表關聯
ORDER BY B_ID;
END IF;
END LOOP;
/*2.3插入二級指標合計行(既可以查詢也可以自己手動添加合計行)*/
INSERT INTO TAB_INDEX_TEMP
SELECT A.ID,
A.INDEXNAME,
A.TOTALWEIGHT,
B.ID,
B.INDEXNAME,
B.INDEXSCORE,
B.COUNTER,
B.REMARK,
B.ISTOTAL,
B.ISCHILD
FROM INDEX_B B, INDEX_A A
WHERE B.STATUS = 1 --二級指標處於啟用狀態(不用擔心一級指標是否禁用問題)
AND B.INDEXTYPE = 2 --二級指標
AND B.ISTOTAL = 1 --合計項
AND B.INDEXA_ID = V_A_ID
AND B.INDEXA_ID = A.ID --兩表關聯
ORDER BY B.ID;
END LOOP;
--3.返回最終數據
OPEN OUT_CURSOR FOR
--查詢臨時表數據作為游標結果集
SELECT * FROM TAB_INDEX_TEMP;
--不能也不用清空臨時表
/*COMMIT;*/
END PRC_INDEX_SEARCH;
第一步:第一個游標CUR_FIRST_INDEX,取每行數據的A_ID當作變量V_A_ID的值,這樣當你的V_A_ID發生變化時,第二個游標CUR_SECOND_INDEX的數據也會發生變化;
第二步:遍歷雙層游標即可。
說明:
1.變量V_A_ID必須得用,如果在定義第二個游標的時候直接用第一個游標的行數據代替,

雖然可以編譯成功,但是,將永遠查不到數據。

所以,當第二個游標的數據依賴於第一個游標的數據時,必須用變量來代替。
2.Oracle的if語句判斷值是否相等,不同於Java和js,使用的是一個等號=;
3. 另外,表示elseif的關鍵詞也與那兩種語言不同,使用的是ELSIF,少一個E;
4.表示賦值的用,:=;
5.不等於使用,<>;
6.在存儲過程里,指定變量的數據類型時,可以動態指定,也就是
一整行數據的數據類型:表名%ROWTYPE,如:ROW_INDEX_A INDEX_A%ROWTYPE;;
某列的數據類型:表名.字段名%TYPE,如:V_A_ID INDEX_A.ID%TYPE;。

4.結果展示
在plsql中選中存儲過程,右鍵--》測試--》F8執行--》右下角游標--》點開查看結果集

層級說明:
----》----》二級
----》----》----》----》三級
----》----》合計
----》----》二級
。。。依此順序循環查詢輸出
寫在最后
哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!
相關推薦:
