PL/SQLでは、SQL文を文字列として組み立てて実行できる「動的SQL」が強力な機能として提供されています。しかし、使い方を誤るとSQLインジェクションのリスクやパフォーマンス低下を招く可能性があります。
この記事では、EXECUTE IMMEDIATE構文を使った動的SQLの基本と、セキュアな実装のためのバインド変数の活用法について詳しく解説します。
動的SQLとは?
動的SQLとは、実行時にSQL文を文字列として構築し、動的に発行する仕組みです。主に次のようなケースで利用されます。
- テーブル名や列名が変数として指定される
- WHERE句などの条件が可変になる
- DDL文(CREATE、ALTERなど)を実行する必要がある
EXECUTE IMMEDIATEの基本構文
EXECUTE IMMEDIATE 'SQL文';
簡単な例として、次のように書くと任意のSQLを実行できます。
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM employees WHERE department_id = 10';
END;
/
ただし、値を直接文字列に埋め込む方法は推奨されません。理由は次のとおりです。
- SQLインジェクションのリスクがある
- 値の型が一致しない場合にエラーになりやすい
- SQLキャッシュの再利用ができず、パフォーマンスが低下する
セキュリティ対策としてのバインド変数
動的SQLでパラメータを渡すには、USING句を使ったバインド変数を利用します。これにより、値とSQL本体を明確に分離できます。
書き方の例(INパラメータ)
DECLARE
v_sql VARCHAR2(1000);
v_dept_id NUMBER := 10;
BEGIN
v_sql := 'DELETE FROM employees WHERE department_id = :1';
EXECUTE IMMEDIATE v_sql USING v_dept_id;
END;
/
このようにすることで、数値や文字列の区別が自動的に処理され、SQLインジェクションのリスクも排除できます。
複数のバインド変数
DECLARE
v_sql VARCHAR2(1000);
v_emp_id NUMBER := 100;
v_salary NUMBER := 5000;
BEGIN
v_sql := 'UPDATE employees SET salary = :1 WHERE employee_id = :2';
EXECUTE IMMEDIATE v_sql USING v_salary, v_emp_id;
END;
/
OUTパラメータの受け取り
バインド変数はOUTパラメータにも対応しています。例えば、次のように使用します。
DECLARE
v_sql VARCHAR2(1000);
v_cnt NUMBER;
BEGIN
v_sql := 'SELECT COUNT(*) INTO :1 FROM employees WHERE department_id = 10';
EXECUTE IMMEDIATE v_sql INTO v_cnt;
DBMS_OUTPUT.PUT_LINE('件数: ' || v_cnt);
END;
/
INTO句はSELECT文に対してのみ使用可能です。
注意すべき点
- テーブル名や列名はバインド変数にできないため、必要な場合は正しくホワイトリストなどで検証してからSQL文に組み込みます。
- SQLが長くなりすぎるとデバッグが困難になるため、ログ出力や
DBMS_SQL
の活用も検討します。 - EXECUTE IMMEDIATEは構文エラーが実行時まで検出されないため、慎重なテストが必要です。
まとめ
動的SQLは柔軟な処理を可能にする反面、セキュリティや保守性の面で注意が必要です。特に、EXECUTE IMMEDIATEを使用する際はバインド変数の利用が必須です。
不正な値の挿入を防ぎ、実行時エラーのリスクを最小限に抑えるためにも、常に「値はSQLとは別に渡す」という原則を意識しましょう。
安全かつ効率的なPL/SQL実装のために、ぜひ本記事の内容を活用してください。