ORA-01036: illegal variable name/number は、Oracleでバインド変数の名前や番号の扱いが不正なときに発生するエラーです。新しいエラーメッセージでは、SQL文内に存在しないバインド変数名をバインドしようとした場合に unrecognized bind variable と表示されることもあります。
よくある原因は、SQL側の :dept_id とアプリ側のパラメータ名が一致していない、値ではなく列名や表名を :table_name のようにバインドしようとしている、DDLでバインドできない場所にプレースホルダを書いている、位置指定バインドの数や順番がずれている、といったパターンです。
ORA-01036は、SQL本文に実際に存在するプレースホルダだけを、実行方式に合った名前または位置でバインドすると解消できます。列名・表名・DDLのオブジェクト名は通常バインドできません。値はバインド変数、SQL構造は許可リストで組み立てる、と分けて考えます。
- ORA-01036とは
- ORA-01008との違い
- SQLにないバインド名を渡しているケース
- 列名や表名をバインドしようとしているケース
- ALTER USERやCREATE USERで発生するケース
- ORDER BYの列名をバインドしているケース
- IN句のリストを1つのバインドで渡しているケース
- 位置指定バインドの番号や順番がずれているケース
- JDBC・ODP.NET・Pythonで名前の指定方法が違うケース
- SQL ServerやMySQLのプレースホルダ形式を混ぜているケース
- EXECUTE IMMEDIATEのUSING句で発生するケース
- 動的SQLの完成形をログで確認する
- ORA-00933・ORA-00936との違い
- 修正チェックリスト
- よくある質問
- まとめ
- 参考
ORA-01036とは
Oracle公式のエラー説明では、ORA-01036はバインド呼び出しへ渡された変数に、SQL文内の対応するプレースホルダがない場合、またはユーザー側でバインドコンテキストを見つけられない場合に発生するとされています。つまり、SQL本文とバインド指定の対応が崩れている状態です。
| 原因 | よくある例 | 修正の方向 |
|---|---|---|
| SQLにない名前を渡している | SQLは :dept_id、アプリは department_id |
プレースホルダ名を一致させる |
| 列名や表名をバインドしている | TRUNCATE TABLE :table_name |
オブジェクト名は許可リストで文字列化する |
| DDLで値以外をバインドしている | ALTER USER :user IDENTIFIED BY :password |
DDLは安全に文字列を組み立てる |
| IN句のリストを1つの値で渡している | IN (:ids) に配列やCSVを渡す |
個数分のプレースホルダを作る |
| 位置指定の数や順番が違う | :1、:2 と値配列が合わない |
出現位置と値を照合する |
| ドライバの書き方が違う | @id や ? をOracle向けSQLへ混ぜる |
利用ドライバのプレースホルダ形式に合わせる |
ORA-01008との違い
ORA-01008 と ORA-01036 はどちらもバインド変数まわりのエラーですが、見る場所が違います。ORA-01008はSQL内にあるプレースホルダへ値が足りない状態、ORA-01036はSQL内にない名前を渡したり、バインドできない場所へプレースホルダを書いた状態です。
| エラー | 主な意味 | 例 |
|---|---|---|
ORA-01008 |
SQL内のバインド変数に値が不足している | :dept_id があるのに値を渡していない |
ORA-01036 |
バインド変数名や番号の指定がSQLと合っていない | SQLにない :department_id を渡している |
値の渡し忘れが疑わしい場合は ORA-01008の原因と解決方法 も確認してください。
SQLにないバインド名を渡しているケース
SQL本文に書いたプレースホルダ名と、アプリ側で追加したパラメータ名が違うとORA-01036になります。リファクタリングでSQLだけ変更した、パラメータ名だけ変更した、という場合に起きやすいです。
-- SQLには :dept_id がある
SELECT *
FROM employees
WHERE department_id = :dept_id;
-- NG: アプリ側では別名 department_id を渡している
-- params = { department_id: 10 }
-- OK: SQL内の名前と同じ名前で渡す
-- params = { dept_id: 10 }
列名や表名をバインドしようとしているケース
バインド変数で置き換えられるのは、基本的に値です。テーブル名、列名、ORDER BYの列名、DDLのオブジェクト名など、SQL構造そのものは通常バインドできません。この誤解はORA-01036のかなり多い原因です。
-- NG: テーブル名はバインド変数にできない TRUNCATE TABLE :table_name; -- NG: 列名も値のようにはバインドできない SELECT :column_name FROM employees WHERE employee_id = :employee_id; -- OK: オブジェクト名は許可リストで選び、値だけをバインドする SELECT employee_name FROM employees WHERE employee_id = :employee_id;
バインド変数の基本は Oracleのバインド変数完全ガイド も参考になります。
ALTER USERやCREATE USERで発生するケース
ALTER USER や CREATE USER のユーザー名、パスワード、表領域名などを、値のようにバインドしようとしてORA-01036になることがあります。DDLのオブジェクト名はバインドできないため、許可した値だけを安全に文字列へ組み立てます。
-- NG: DDLのユーザー名やパスワードを値のようにバインドしようとしている ALTER USER :user_name IDENTIFIED BY :password; -- OKの考え方: ユーザー名は許可リストやDBMS_ASSERTで検証してから組み立てる -- v_sql := 'ALTER USER ' || safe_user_name || ' IDENTIFIED BY "' || safe_password || '"'; -- EXECUTE IMMEDIATE v_sql;
ORDER BYの列名をバインドしているケース
一覧画面で並び替え列を変えたいときに、ORDER BY :sort_column のように書くと、列名ではなく値として扱われます。期待どおりに並ばないだけでなく、ドライバや実行方法によってはORA-01036の原因になります。
-- NG: 列名はバインド変数にしない SELECT employee_id, employee_name, hire_date FROM employees ORDER BY :sort_column; -- OKの考え方: sort_column は employee_id / employee_name / hire_date だけ許可する SELECT employee_id, employee_name, hire_date FROM employees ORDER BY hire_date;
IN句のリストを1つのバインドで渡しているケース
IN (:ids) に配列やカンマ区切り文字列を1つ渡して、複数値として扱わせようとする実装もよくあります。Oracleでは、単純なバインド変数1つが自動で複数のプレースホルダへ展開されるわけではありません。ドライバや実装によってはORA-01036や別のバインドエラーにつながります。
-- NGの考え方: :ids 1つに [10, 20, 30] を渡して複数値にしたい
SELECT *
FROM employees
WHERE department_id IN (:ids);
-- OK: 値の個数分だけプレースホルダを作る
SELECT *
FROM employees
WHERE department_id IN (:id1, :id2, :id3);
-- params = { id1: 10, id2: 20, id3: 30 }
件数が多い場合は、一時表、コレクション、JSON_TABLEなど別の渡し方も検討します。IN句の基本と注意点は OracleのIN句の使い方 も参考になります。
位置指定バインドの番号や順番がずれているケース
:1、:2 のような位置指定では、SQL内の出現位置と値の配列を対応させます。番号の見た目だけに頼ると、ドライバ側の扱いとずれてORA-01036やORA-01008につながります。
-- SQLには2つの位置指定バインドがある SELECT * FROM employees WHERE department_id = :1 AND status = :2; -- OK: 出現位置に合わせて2つ渡す -- values = [10, 'ACTIVE']
JDBC・ODP.NET・Pythonで名前の指定方法が違うケース
アプリのドライバによって、パラメータ名にコロンを含めるか、含めないか、位置指定か名前指定かが異なります。SQL側はOracleの :name 形式でも、APIに渡す名前は name にする、という実装もあります。ODP.NETでは BindByName の設定によって、名前で対応するか位置で対応するかが変わる点にも注意します。
-- SQL側 SELECT * FROM employees WHERE employee_id = :employee_id -- NG例: ドライバの想定と違う名前を渡している -- ParameterName = ':employee_id' なのか 'employee_id' なのかを混同 -- OK: 利用しているドライバの仕様に合わせる -- SQL内のプレースホルダ名と、APIに渡すパラメータ名の対応をログで確認する
SQL ServerやMySQLのプレースホルダ形式を混ぜているケース
SQL Serverの @id、ODBCの ?、MySQL系のプレースホルダをOracle向けSQLへ混ぜると、ドライバや実行方法によってバインドとして認識されません。Oracle向けには、利用ドライバが期待する形式へ合わせます。
-- NG: SQL Server風の @id をOracle SQLへそのまま混ぜている SELECT * FROM employees WHERE employee_id = @employee_id; -- OK: Oracleの名前付きバインドに合わせる SELECT * FROM employees WHERE employee_id = :employee_id;
無効な文字として落ちる場合は ORA-00911の原因と解決方法 も確認してください。
EXECUTE IMMEDIATEのUSING句で発生するケース
PL/SQLの EXECUTE IMMEDIATE では、動的SQL内のプレースホルダと USING 句の値を対応させます。SQL内にない名前を渡す、またはSQL構造をバインドしようとするとORA-01036の原因になります。
-- NG: テーブル名をバインドしようとしている
DECLARE
v_sql VARCHAR2(1000);
v_count NUMBER;
BEGIN
v_sql := 'SELECT COUNT(*) FROM :table_name WHERE employee_id = :employee_id';
EXECUTE IMMEDIATE v_sql INTO v_count USING 'EMPLOYEES', 100;
END;
/
-- OK: テーブル名は検証済み文字列、値だけをバインドする
DECLARE
v_sql VARCHAR2(1000);
v_count NUMBER;
BEGIN
v_sql := 'SELECT COUNT(*) FROM employees WHERE employee_id = :employee_id';
EXECUTE IMMEDIATE v_sql INTO v_count USING 100;
END;
/
EXECUTE IMMEDIATE の基本は OracleのEXECUTE IMMEDIATE完全ガイド も参考になります。
動的SQLの完成形をログで確認する
ORA-01036は、ソースコードの断片だけ見ても原因が分かりにくいことがあります。条件分岐や文字列連結が終わった後のSQL全文と、渡しているバインド名・値の一覧を同じログに出すと切り分けやすくなります。
| 確認項目 | 見る内容 |
|---|---|
| SQL本文 | :employee_id など実際に出ているプレースホルダ |
| バインド名 | SQL本文に存在する名前だけを渡しているか |
| バインド値 | 値の数、NULL、型が意図どおりか |
| SQL構造 | 列名・表名・ORDER BY列を値のようにバインドしていないか |
ORA-00933・ORA-00936との違い
ORA-01036 はバインド変数の指定が主な原因です。一方、ORA-00933 は句の終わり方や他DB構文、ORA-00936 は式不足が中心です。
| エラー | 主な原因 | 確認する場所 |
|---|---|---|
ORA-01036 |
バインド名、番号、バインドできない場所 | SQL内の :name とアプリのパラメータ |
ORA-01008 |
バインド値の不足 | SQL内のプレースホルダ数と値の数 |
ORA-00933 |
句の順序、SQLの終わり方、他DB構文 | LIMIT、UPDATE JOIN、余分な句 |
ORA-00936 |
式、列、値、条件の不足 | SELECT 句、WHERE 句、関数引数 |
句の終わり方が怪しい場合は ORA-00933の原因と解決方法、式不足なら ORA-00936の原因と解決方法 を確認してください。
修正チェックリスト
| 手順 | 確認すること | 見るポイント |
|---|---|---|
| 1 | SQL本文をログに出す | 実際に :name が存在するか |
| 2 | 渡しているパラメータ名を見る | SQLにない名前を渡していないか |
| 3 | 列名・表名をバインドしていないか確認する | 値とSQL構造を分けているか |
| 4 | DDLを確認する | ユーザー名、表名、列名を値のようにバインドしていないか |
| 5 | IN句のリストを見る | IN (:ids) に配列やCSVを1個で渡していないか |
| 6 | 位置指定の数と順番を見る | :1、:2 と値配列が合っているか |
| 7 | ドライバの記法を確認する | コロン付き/なし、名前指定/位置指定を混同していないか |
| 8 | ORA-01008と切り分ける | 不足なのか、不正な名前・場所なのかを分ける |
よくある質問
ORA-01036とORA-01008は何が違いますか?
ORA-01008はSQL内のプレースホルダに値が不足している状態です。ORA-01036は、SQL内にない名前を渡したり、列名や表名などバインドできない場所へプレースホルダを書いた状態です。
テーブル名をバインド変数にできますか?
通常はできません。テーブル名や列名はSQL構造なので、許可リストで検証して文字列として組み立てます。データ値だけをバインド変数にします。
ParameterNameにはコロンを付けますか?
利用するドライバやAPIによります。SQL本文では :employee_id と書き、API側では employee_id を指定する実装もあります。ドライバ仕様と実際のログで確認します。
IN句に配列を1つ渡せますか?
単純な IN (:ids) に配列やカンマ区切り文字列を渡しても、自動で複数値へ展開されるわけではありません。値の個数分だけ :id1, :id2, :id3 のようにプレースホルダを作るか、別の渡し方を検討します。
ALTER USERでORA-01036が出るのはなぜですか?
ユーザー名やパスワードなどDDLの構造部分を値のようにバインドしようとしている可能性があります。オブジェクト名は検証済み文字列として組み立て、値バインドとは分けます。
まとめ
ORA-01036は、バインド変数名や番号、またはバインドする場所がSQL本文と合っていないときに発生します。まず完成したSQLと、渡しているパラメータ名・値の一覧を並べて確認しましょう。
値はバインド変数で渡し、列名・表名・DDLのオブジェクト名は許可リストで検証してからSQLへ組み込みます。ORA-01008との違いを押さえると、値不足なのか、名前や場所の不正なのかを切り分けやすくなります。
参考
ORA-01036 – Oracle Database Error Help
Native Dynamic SQL – Oracle Database PL/SQL Language Reference
Database Object Names and Qualifiers – Oracle SQL Language Reference
