OracleのPL/SQL基本文法【初心者向け完全入門ガイド】

当ページのリンクには広告が含まれています。

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_FOUNDSELECT INTOで0行
TOO_MANY_ROWSSELECT INTOで2行以上
DUP_VAL_ON_INDEXUNIQUE制約違反のINSERT/UPDATE
VALUE_ERROR型変換エラーや桁あふれ
ZERO_DIVIDE0除算

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;

関連記事:

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

ITの事や自分の経験談など綴っていきたいと思っています。

コメント

コメントする

CAPTCHA


目次