#PL/SQL カーソルについて
PL/SQLにて、通常はSELECT文で抽出したデータを変数に代入して処理をするということがよくありますが、カーソルを使用すると、データベースの表から取り出した複数行を処理することができます。
##カーソル処理の流れ
CREATE [OR REPLACE] PROCEDURE
ストアドプロシージャ名[(引数名 {IN | OUT | INOUT} データ型,...)]
IS
カーソルの定義(CURSOR [カーソル名] IS [SELECT文])
BEGIN
カーソルのオープン(OPEN [カーソル名])
LOOP
データの取得(FETCH [カーソル名] INTO [変数名])
END LOOP;
カーソルのクローズ(CLOSE [カーソル名])
END;
##カーソルの使用例
-
1件のデータを変数に格納する(カーソル処理とは異なるが、初歩的な事なので記述)
必ずデータがある事を条件にINTO句で変数に挿入する -
配列(索引付き変数)の操作
項目数分の変数を用意する必要があるが、項目単位の操作が可能(7.のバルクバインド処理にも使える) -
レコード単位の操作
FETCH INTO を使って1レコード単位で処理(通常、ループ文としてはあまり使えない) -
索引付きレコード単位の操作
レコード&項目の2元配列操作が可能 -
カーソル・ループの操作
OPEN,FETCH,CLOSEが省略可能(3.より便利) -
暗黙カーソル
直前のSQL実行結果が必要な場合に使える -
バルクバインド処理(1.のデータを違う表にセット)
SQLエンジンを1回しか使用しない為、処理が高速 -
WHILE文を使ったカーソル処理の例
CREATE PROCEDURE CURSOR_PROCEDURE(IX IN NUMBER,RESULT0 OUT CHAR,RESULT1 OUT CHAR,RESULT2 OUT CHAR,RESULT3 OUT CHAR,RESULT4 OUT CHAR,RESULT5 OUT CHAR,RESULT6 OUT CHAR)
IS
CURSOR TABLE01_C IS SELECT HOGE,FUGA,ROWNUM A FROM TABLE01;
TYPE TYP_TAB_1 IS TABLE OF TABLE01.HOGE%TYPE INDEX BY BINARY_INTEGER; --pattern②
TYPE TYP_TAB_2 IS TABLE OF TABLE01.FUGA%TYPE INDEX BY BINARY_INTEGER; --pattern②
TYPE TYP_REC_A IS RECORD(HOGE TABLE01.HOGE%TYPE,FUGA TABLE01.FUGA%TYPE,A NUMBER); --pattern③
TYPE TYP_TAB_B IS TABLE OF TYP_REC_A INDEX BY BINARY_INTEGER; --pattern④
TAB_1 TYP_TAB_1; --pattern②
TAB_2 TYP_TAB_2; --pattern②
REC_A TYP_REC_A; --pattern③
RECTAB_A TYP_TAB_B; --pattern④
IX1 NUMBER:=1; --pattern③
IX2 NUMBER:=1; --pattern④
IX3 NUMBER; --pattern⑥
IX4 NUMBER:=1; --pattern⑦
BEGIN
/* ① 1件のデータを変数に格納する(カーソル処理とは異なるが、初歩的な事なので記述) */
SELECT NVL(MAX(HOGE||FUGA),'NO_DATA')INTO RESULT0 FROM TABLE01 WHERE HOGE || FUGA || SECTION =
(SELECT HOGE||FUGA||SECTION FROM (SELECT HOGE,FUGA,SECTION,ROWNUM A FROM TABLE01) WHERE A = IX);
/* ② 配列(索引付き変数)の操作 */
FOR TABLE01_R IN TABLE01_C LOOP
TAB_1(TABLE01_R.A):=TABLE01_R.HOGE;
TAB_2(TABLE01_R.A):=TABLE01_R.FUGA;
END LOOP;
IF NOT(TAB_1.EXISTS(IX))THEN
RESULT1:='NO_DATA';
ELSE
RESULT1:=TAB_1(IX)||TAB_2(IX);
END IF;
/* ③ レコード単位の操作 */
RESULT2:='NO_DATA';
OPEN TABLE01_C;
LOOP
FETCH TABLE01_C INTO REC_A;
IF IX=IX1 THEN
RESULT2:=REC_A.HOGE||REC_A.FUGA;
END IF;
EXIT WHEN TABLE01_C%NOTFOUND;
IX1:=IX1+1;
END LOOP;
CLOSE TABLE01_C;
/* ④ 索引付きレコード単位の操作 */
FOR TABLE01_R IN TABLE01_C LOOP
RECTAB_A(TABLE01_R.A):=TABLE01_R;
END LOOP;
IF NOT(RECTAB_A.EXISTS(IX))THEN
RESULT3:='NO_DATA';
ELSE
RESULT3:=RECTAB_A(IX).HOGE||RECTAB_A(IX).FUGA;
END IF;
/* ⑤ カーソル・ループの操作 */
RESULT4:='NO_DATA';
FOR TABLE01_R IN TABLE01_C LOOP
IF IX2=IX THEN
RESULT4:=TABLE01_R.HOGE||TABLE01_R.FUGA;
EXIT;
END IF;
IX2:=IX2+1;
END LOOP;
/* ⑥ 暗黙カーソル */
UPDATE TABLE01 SET HOGE=HOGE WHERE HOGE || FUGA || SECTION =
(SELECT HOGE||FUGA||SECTION FROM (SELECT HOGE,FUGA,SECTION,ROWNUM A FROM TABLE01) WHERE A = IX);
IF SQL%FOUND THEN
RESULT5:=SQL%ROWCOUNT;
ELSE
RESULT5:='NO_DATA';
END IF;
/* ⑦ バルクバインド処理(①のデータを違う表にセット) */
DELETE FROM TABLE01_TEST;
FORALL IX3 IN 1..TAB_1.COUNT INSERT INTO TABLE01_TEST VALUES(TAB_1(IX3),TAB_2(IX3));
/* ⑧ WHILE文を使ったカーソル処理の例 */
RESULT6:='NO_DATA';
OPEN TABLE01_C;
FETCH TABLE01_C INTO REC_A;
WHILE TABLE01_C%FOUND LOOP
IF IX=IX4 THEN
RESULT6:=REC_A.HOGE||REC_A.FUGA;
EXIT;
END IF;
FETCH TABLE01_C INTO REC_A;
IX4:=IX4+1;
END LOOP;
CLOSE TABLE01_C;
EXCEPTION
WHEN OTHERS THEN ROLLBACK;
DBMS_OUTPUT.PUT_LINE('Error: '||SQLERRM||'('||SQLCODE||')');
END;
/