PL/SQLとはOracleのSQL拡張言語で、変数・条件分岐・ループ・例外処理などのプログラム的な処理をDB内で実行できます。本記事ではPL/SQLを一度も書いたことがない方向けに基本文法から実用的なサンプルまで解説します。
目次
1. PL/SQLとは {#about}
PL/SQL(Procedural Language / SQL)はOracleが開発したSQL拡張言語です。
PL/SQLでできること:
- SQLに変数・条件分岐・ループを組み合わせた処理
- ストアドプロシージャ・ファンクション・トリガーの作成
- エラー処理(例外ハンドリング)
- カーソルで複数行を1行ずつ処理
PL/SQLの実行単位(ブロック):
DECLARE -- 変数宣言セクション(省略可)
BEGIN -- 処理セクション(必須)
EXCEPTION -- 例外処理セクション(省略可)
END;
/ -- ブロックの終わり(/で実行)2. PL/SQLの基本構造(ブロック) {#block}
最もシンプルなブロック
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello, Oracle!');
END;
/実行前にSET SERVEROUTPUTを有効化
-- SQL*Plusで出力を表示するために必要
SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello, Oracle!');
END;
/3. 変数の宣言と代入 {#variables}
DECLARE
-- 変数宣言(変数名 データ型 := 初期値)
v_name VARCHAR2(100) := 'Oracle';
v_count NUMBER := 0;
v_today DATE := SYSDATE;
v_flag BOOLEAN := TRUE;
-- テーブルの列と同じ型を使う(%TYPE)
v_emp_name employees.last_name%TYPE;
-- テーブルの行と同じ型を使う(%ROWTYPE)
v_emp_row employees%ROWTYPE;
BEGIN
-- 代入
v_count := 100;
v_name := 'PL/SQL';
-- SELECTで変数に値を取得(必ず1行返る必要がある)
SELECT last_name INTO v_emp_name
FROM employees
WHERE employee_id = 100;
-- 行全体を取得
SELECT * INTO v_emp_row
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('社員名: ' || v_emp_row.last_name);
DBMS_OUTPUT.PUT_LINE('給与: ' || v_emp_row.salary);
END;
/4. 条件分岐(IF / CASE) {#if-case}
IF文
DECLARE
v_salary NUMBER := 60000;
BEGIN
IF v_salary >= 80000 THEN
DBMS_OUTPUT.PUT_LINE('高給与');
ELSIF v_salary >= 50000 THEN
DBMS_OUTPUT.PUT_LINE('中給与');
ELSE
DBMS_OUTPUT.PUT_LINE('低給与');
END IF;
END;
/CASE文
DECLARE
v_grade CHAR(1) := 'B';
v_msg VARCHAR2(50);
BEGIN
CASE v_grade
WHEN 'A' THEN v_msg := '優秀';
WHEN 'B' THEN v_msg := '良好';
WHEN 'C' THEN v_msg := '普通';
ELSE v_msg := '要改善';
END CASE;
DBMS_OUTPUT.PUT_LINE('評価: ' || v_msg);
END;
/5. ループ処理(LOOP / FOR / WHILE) {#loop}
基本LOOP(EXIT WHENで抜ける)
DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('カウント: ' || v_i);
v_i := v_i + 1;
EXIT WHEN v_i > 5; -- 条件を満たしたら抜ける
END LOOP;
END;
/FOR LOOPで1〜10を出力
BEGIN
FOR i IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('i = ' || i);
END LOOP;
END;
/WHILE LOOP
DECLARE
v_count NUMBER := 0;
BEGIN
WHILE v_count < 5 LOOP
v_count := v_count + 1;
DBMS_OUTPUT.PUT_LINE('count = ' || v_count);
END WHILE;
END;
/6. カーソルでSELECT結果を処理する {#cursor}
複数行をSELECTして1行ずつ処理する場合はカーソルを使います。
暗黙カーソル(FOR IN ループ)—最もシンプル
BEGIN
-- employees を1行ずつ処理
FOR emp_rec IN (SELECT employee_id, last_name, salary FROM employees WHERE department_id = 10)
LOOP
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id || ': ' || emp_rec.last_name || ' - ' || emp_rec.salary);
END LOOP;
END;
/明示カーソル(CURSOR宣言あり)
DECLARE
-- カーソルを宣言
CURSOR emp_cursor IS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = 10
ORDER BY last_name;
v_emp emp_cursor%ROWTYPE;
BEGIN
-- カーソルを開く
OPEN emp_cursor;
LOOP
-- 1行フェッチ
FETCH emp_cursor INTO v_emp;
EXIT WHEN emp_cursor%NOTFOUND; -- データなしで抜ける
DBMS_OUTPUT.PUT_LINE(v_emp.last_name || ': ' || v_emp.salary);
END LOOP;
-- カーソルを閉じる
CLOSE emp_cursor;
END;
/7. 例外処理(EXCEPTION) {#exception}
エラーが発生したときの処理をEXCEPTIONブロックに記述します。
DECLARE
v_emp employees%ROWTYPE;
BEGIN
SELECT * INTO v_emp
FROM employees
WHERE employee_id = 99999; -- 存在しないID
DBMS_OUTPUT.PUT_LINE('社員名: ' || v_emp.last_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('エラー: 対象の社員が見つかりません。');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('エラー: 複数の行が返されました。');
WHEN OTHERS THEN
-- 予期しないエラーをキャッチ
DBMS_OUTPUT.PUT_LINE('予期しないエラー: ' || SQLERRM);
RAISE; -- 上位にエラーを再発生させる
END;
/よく使う定義済み例外:
| 例外名 | 発生条件 |
|---|---|
| NO_DATA_FOUND | SELECT INTOで0行 |
| TOO_MANY_ROWS | SELECT INTOで2行以上 |
| DUP_VAL_ON_INDEX | UNIQUE制約違反のINSERT/UPDATE |
| VALUE_ERROR | 型変換エラーや桁あふれ |
| ZERO_DIVIDE | 0除算 |
8. プロシージャとファンクションの作成 {#procedure-function}
ストアドプロシージャ(値を返さない)
-- プロシージャの作成
CREATE OR REPLACE PROCEDURE update_salary(
p_emp_id IN NUMBER, -- 入力パラメータ
p_rate IN NUMBER, -- 昇給率(例:1.1 = 10%アップ)
p_result OUT VARCHAR2 -- 出力パラメータ
)
IS
v_count NUMBER;
BEGIN
UPDATE employees
SET salary = salary * p_rate
WHERE employee_id = p_emp_id;
v_count := SQL%ROWCOUNT; -- 更新行数
IF v_count = 0 THEN
p_result := '対象の社員が見つかりません: ' || p_emp_id;
ELSE
COMMIT;
p_result := '給与を更新しました: 社員ID ' || p_emp_id;
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
p_result := 'エラー: ' || SQLERRM;
END update_salary;
/
-- プロシージャの実行
DECLARE
v_msg VARCHAR2(200);
BEGIN
update_salary(100, 1.1, v_msg);
DBMS_OUTPUT.PUT_LINE(v_msg);
END;
/ストアドファンクション(値を返す)
-- ファンクションの作成(SQLから直接呼び出せる)
CREATE OR REPLACE FUNCTION get_dept_headcount(
p_dept_id IN NUMBER
) RETURN NUMBER
IS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count
FROM employees
WHERE department_id = p_dept_id;
RETURN v_count;
END get_dept_headcount;
/
-- SQLから呼び出す
SELECT department_id, get_dept_headcount(department_id) AS 人数
FROM departments;9. まとめ:PL/SQLの構文早見表 {#summary}
-- 変数宣言
v_name VARCHAR2(100) := '初期値';
v_col table.column%TYPE;
v_row table%ROWTYPE;
-- IF文
IF 条件 THEN ... ELSIF 条件 THEN ... ELSE ... END IF;
-- FOR LOOP
FOR i IN 1..10 LOOP ... END LOOP;
FOR rec IN (SELECT ...) LOOP ... END LOOP;
-- CURSOR
OPEN カーソル; FETCH カーソル INTO 変数; CLOSE カーソル;
-- 例外処理
EXCEPTION
WHEN NO_DATA_FOUND THEN ...
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM);
-- プロシージャ
CREATE OR REPLACE PROCEDURE 名前(引数 IN/OUT 型) IS BEGIN ... END;
-- ファンクション
CREATE OR REPLACE FUNCTION 名前(引数 IN 型) RETURN 型 IS BEGIN RETURN 値; END;関連記事:









コメント