【Oracle】ORA-01006の原因と解決方法|bind variable does not exist・バインド変数が存在しない時の直し方

【Oracle】ORA-01006の原因と解決方法|bind variable does not exist・バインド変数が存在しない時の直し方 Oracle

ORA-01006: bind variable does not exist は、Oracleで実行時に渡したバインド変数が、実際のSQL文やPL/SQLブロック内に存在しない時に発生するエラーです。SQLには :dept_id しかないのにアプリ側で :department_id を渡している、動的SQLで条件を外したのに USING 句だけ残っている、といった状況で起きます。

Oracle公式の説明では、バインド呼び出しで参照した変数がSQL文に列挙されていないことが原因です。つまり、SQL本文にあるプレースホルダと、実行時に渡しているバインド指定の対応が崩れています。

先に結論
ORA-01006が出たら、完成後のSQL全文とバインド値一覧を同時にログへ出し、SQLに存在しない名前や余分な値を渡していないか確認します。不足なら ORA-01008、名前や場所の不正なら ORA-01036 も合わせて切り分けます。
スポンサーリンク

ORA-01006とは

バインド変数は、SQL内の値を :name:1 のようなプレースホルダで表し、実行時に値だけを別途渡す仕組みです。安全性やパース効率の面では有効ですが、SQL本文と渡す値の対応がずれるとバインド系のエラーになります。

見る場所 ORA-01006で起きていること 修正の方向
SQL本文 渡された名前のプレースホルダがない SQLにある名前だけを渡す
動的SQL 条件を削ったのにUSING側だけ残った SQL生成と値追加を同じ分岐にする
DBMS_SQL 存在しない名前をBIND_VARIABLEしている パースしたSQLのプレースホルダ名へ合わせる
アプリ パラメータ名のタイプミスや余分な値がある 最終SQLとパラメータ一覧を照合する

バインド変数の基本や性能面は Oracleバインド変数ガイド にまとめています。この記事では、ORA-01006の原因になる「SQLにないバインドを渡す」パターンに絞って解説します。

ORA-01008・ORA-01036との違い

ORA-01006は、ORA-01008やORA-01036と混同しやすいエラーです。切り分けでは、SQL本文にあるプレースホルダと、実行時に渡している値のどちらが多いか、または名前や場所が不正かを見ます。

ORA-01006

主な意味: SQLに存在しないバインド変数を渡している

例: SQLには :dept_id しかないのに :department_id を渡す

確認先: 余分なバインド、名前のタイプミス、動的SQLの条件分岐

ORA-01008

主な意味: SQL内のバインド変数に値が不足している

例: SQLに :dept_id があるのに値を渡していない

関連記事: ORA-01008

ORA-01036

主な意味: バインド変数名や番号、バインドできない場所の指定が不正

例: 表名・列名・DDLのオブジェクト名をバインドしようとしている

関連記事: ORA-01036

SQLにないバインド変数を渡している

最も単純な原因は、SQL本文にない名前を実行時に渡しているケースです。パラメータ名の変更、リファクタリング、条件分岐の追加で起きやすいです。

ora01006-extra-bind-name.sql
-- SQLにあるのは :dept_id だけ
SELECT employee_id, employee_name
FROM employees
WHERE department_id = :dept_id;

-- NG: 実行側で :department_id を渡していると対応先がない
-- bind: department_id = 10

修正は、SQL側と実行側の名前をそろえることです。名前指定バインドでは、変数名の表記ゆれを減らすため、SQLを組み立てる層とパラメータを作る層で同じ定数を使うと事故が減ります。

ora01006-extra-bind-fixed.sql
SELECT employee_id, employee_name
FROM employees
WHERE department_id = :dept_id;

-- OK: SQLに存在する :dept_id に値を渡す
-- bind: dept_id = 10

動的SQLで条件を外したのにUSING句だけ残っている

実務で多いのは、検索条件を動的に組み立てる処理です。条件を追加しなかった時にも USING 句へ値だけ渡してしまうと、SQL内に対応するプレースホルダがないためORA-01006になります。

dynamic-sql-using-extra-bind.sql
DECLARE
  v_sql   VARCHAR2(4000) := 'SELECT * FROM employees WHERE 1 = 1';
  v_name  VARCHAR2(100) := NULL;
  v_count NUMBER;
BEGIN
  IF v_name IS NOT NULL THEN
    v_sql := v_sql || ' AND employee_name LIKE :name';
  END IF;

  -- NG: v_name がNULLならSQLに :name が存在しない
  EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM (' || v_sql || ')'
    INTO v_count
    USING v_name;
END;
/

条件を追加する分岐と、バインド値を渡す分岐は必ずセットにします。静的に分岐できる範囲なら、SQL文自体を分ける方が読みやすいこともあります。

dynamic-sql-using-branch-fixed.sql
DECLARE
  v_sql   VARCHAR2(4000) := 'SELECT COUNT(*) FROM employees WHERE 1 = 1';
  v_name  VARCHAR2(100) := NULL;
  v_count NUMBER;
BEGIN
  IF v_name IS NOT NULL THEN
    v_sql := v_sql || ' AND employee_name LIKE :name';
    EXECUTE IMMEDIATE v_sql INTO v_count USING '%' || v_name || '%';
  ELSE
    EXECUTE IMMEDIATE v_sql INTO v_count;
  END IF;
END;
/

動的SQLでは、動的SQLのセキュアな書き方 で解説しているように、値はバインド変数、列名や表名などSQL構造は許可リストや DBMS_ASSERT で扱うのが基本です。

同じプレースホルダ名を複数回使う場合

動的SQLでは、同じ名前のプレースホルダを複数回書いた時の扱いにも注意します。通常のSQL文では出現回数ぶんの値を USING へ渡します。一方、匿名PL/SQLブロックや CALL 文では、同じ名前は1つのバインド変数として扱われます。この違いを混同すると、値が足りない場合はORA-01008、余分な値や対応しない名前を渡す場合はORA-01006系の原因になります。

repeated-placeholder-sql-statement.sql
DECLARE
  v_sql  VARCHAR2(4000);
  v_dept NUMBER := 10;
  v_cnt  NUMBER;
BEGIN
  v_sql := 'SELECT COUNT(*)
            FROM employees
            WHERE department_id = :x
               OR manager_id = :x';

  -- SQL文では :x が2回出ているので、値も2回渡す
  EXECUTE IMMEDIATE v_sql INTO v_cnt USING v_dept, v_dept;
END;
/
repeated-placeholder-plsql-block.sql
DECLARE
  v_block VARCHAR2(4000);
  v_dept  NUMBER := 10;
BEGIN
  v_block := 'BEGIN update_dept(:x, :x); END;';

  -- PL/SQLブロックでは同じ名前 :x は1つの値に対応する
  EXECUTE IMMEDIATE v_block USING v_dept;
END;
/

迷った時は、実行している文字列が「SQL文」なのか「匿名PL/SQLブロック」なのかを先に分けます。BEGIN ... END; で包んでいる場合はPL/SQLブロックとして扱われるため、通常のSQL文とは数え方が変わります。

OPEN FORのUSING句がSQL本文と合っていない

REF CURSORを返す処理でも同じです。OPEN cursor FOR sql USING ... では、SQL本文にあるプレースホルダに対応する値だけを USING に渡します。

open-for-extra-bind.sql
DECLARE
  TYPE rc IS REF CURSOR;
  c      rc;
  v_sql  VARCHAR2(4000) := 'SELECT * FROM employees';
  v_dept NUMBER := 10;
BEGIN
  -- NG: SQLに :dept_id がないのに v_dept を渡している
  OPEN c FOR v_sql USING v_dept;
END;
/
open-for-extra-bind-fixed.sql
DECLARE
  TYPE rc IS REF CURSOR;
  c      rc;
  v_sql  VARCHAR2(4000) := 'SELECT * FROM employees WHERE department_id = :dept_id';
  v_dept NUMBER := 10;
BEGIN
  OPEN c FOR v_sql USING v_dept;
END;
/

DBMS_SQL.BIND_VARIABLEの名前が間違っている

DBMS_SQL を使う場合は、PARSE したSQL内のプレースホルダ名と、BIND_VARIABLE で指定する名前を合わせます。SQLにない名前を指定すると、ORA-01006の原因になります。

dbms-sql-bind-variable-name.sql
DECLARE
  c INTEGER;
  n INTEGER;
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(
    c,
    'SELECT * FROM employees WHERE department_id = :dept_id',
    DBMS_SQL.NATIVE
  );

  -- NG: SQLには :department_id が存在しない
  DBMS_SQL.BIND_VARIABLE(c, ':department_id', 10);
  n := DBMS_SQL.EXECUTE(c);
  DBMS_SQL.CLOSE_CURSOR(c);
END;
/
dbms-sql-bind-variable-fixed.sql
DBMS_SQL.BIND_VARIABLE(c, ':dept_id', 10);

JDBCやPythonなどアプリ側で起きる場合

アプリから実行する場合は、最終的に送るSQLとパラメータの名前をログで並べて確認します。フレームワークやドライバによっては同じ状況が ORA-01036 として出ることもありますが、切り分けの基本は「SQLにある名前だけを渡す」です。

python-oracledb-extra-param.py
sql = """
SELECT employee_id, employee_name
FROM employees
WHERE department_id = :dept_id
"""

# NG: SQLにない department_id を渡している
cursor.execute(sql, {"department_id": 10})

# OK
cursor.execute(sql, {"dept_id": 10})
jdbc-extra-bind-name.java
String sql = "SELECT employee_id FROM employees WHERE department_id = :dept_id";

// 名前付きパラメータを扱うラッパーを使う場合、SQL側の名前と一致させる
params.put("dept_id", 10);

// department_id のような別名を渡すと、SQL側に対応先がない

調査手順

ORA-01006は、完成後のSQLとバインド一覧を見ればかなり早く切り分けられます。ソースコード上の断片ではなく、条件分岐や文字列連結が終わった後のSQLを確認してください。

順番 確認すること 見るポイント
1 完成後のSQLをログに出す :name:1 の出現を確認する
2 渡しているバインド一覧を出す SQLにない名前や余分な値がないか見る
3 条件分岐を確認する SQL追加と値追加が同じ条件で動くか見る
4 ORA-01008と分ける 不足なのか、余分なのかを分ける
5 ORA-01036と分ける 表名・列名・DDLなど、バインドできない場所でないか見る

チェックリスト

  • 完成後のSQL全文を確認した
  • SQL内のプレースホルダ名と実行時パラメータ名が一致している
  • 動的SQLで、条件追加とバインド値追加が同じ分岐にある
  • SQLにない余分なパラメータを渡していない
  • DBMS_SQL.BIND_VARIABLE の名前がSQL本文と一致している
  • 値はバインド変数、表名・列名は許可リストで扱っている
  • 不足ならORA-01008、名前/場所の不正ならORA-01036として切り分けた

よくある質問

ORA-01006とORA-01008は何が違いますか?

ORA-01006は、SQLに存在しないバインド変数を渡している状態です。ORA-01008は、SQLに存在するバインド変数へ値が不足している状態です。値が多い・名前が余っているならORA-01006、不足しているならORA-01008を疑います。

同じSQLなのに環境でだけ発生します

SQL生成の条件分岐、アプリ設定、検索条件の有無、ドライバのバージョン差で、最終SQLや渡すパラメータが変わっている可能性があります。エラー時のSQL全文とバインド一覧を同じログに出してください。

表名や列名をバインドできますか?

通常、表名や列名などSQL構造はバインド変数では置き換えません。値はバインド変数、SQL構造は許可リストで選択し、必要に応じてDBMS_ASSERTで検証します。

まとめ

ORA-01006は、SQLに存在しないバインド変数を実行時に渡している時に発生します。完成後のSQL本文とバインド一覧を照合し、余分な名前、タイプミス、動的SQLの条件分岐ずれを確認しましょう。

不足ならORA-01008、バインド名や場所の不正ならORA-01036と切り分けると、原因を早く絞れます。動的SQLでは、SQL文字列とバインド値を同じ分岐で組み立てるのが安全です。

参考

ORA-01006 – Oracle Database Error Help

PL/SQL Dynamic SQL – Oracle Database PL/SQL Language Reference