【Oracle】PLS-00323の原因と解決方法|package specification and bodyの不一致

【Oracle】PLS-00323の原因と解決方法|package specification and bodyの不一致 Oracle

PLS-00323: subprogram or cursor 'xxx' is declared in a package specification and must be defined in the package body は、Oracle PL/SQLのパッケージ仕様部に宣言したプロシージャ、ファンクション、カーソルが、パッケージ本体で対応する形で定義されていない時に発生するコンパイルエラーです。

典型的には、仕様部に PROCEDURE close_order と書いたのに本体に書き忘れた、本体側の名前・引数・戻り値・%TYPE の書き方が仕様部と一致していない、カーソル宣言とカーソル定義が対応していない、といったケースで発生します。

先に結論
PLS-00323では、パッケージ仕様部とパッケージ本体を横に並べて、対象サブプログラムまたはカーソルの見出しが一致しているか確認します。Oracle公式ドキュメントでも、仕様部のカーソル/サブプログラム宣言には本体側の対応する定義が必要で、対応するサブプログラムの見出しは空白を除いて一致する必要があると説明されています。
スポンサーリンク

PLS-00323とは

PLS-00323は、パッケージ仕様部とパッケージ本体の契約が合っていない時のエラーです。仕様部は外部に公開するインターフェース、本体はその実装です。仕様部に公開したサブプログラムやカーソルは、本体で同じ見出しの定義を持つ必要があります。

パッケージ本体をコンパイルした時に Warning: Package Body created with compilation errors と表示され、SHOW ERRORSUSER_ERRORS を見るとPLS-00323が出ている、という流れがよくあります。

状況 起きていること 見る場所
仕様部に宣言した処理が本体にない 実装書き忘れ PACKAGEPACKAGE BODY
名前が微妙に違う 別のサブプログラムとして扱われる プロシージャ名・ファンクション名
引数の型や順番が違う 見出しが一致しない 引数リスト、%TYPE、デフォルト値
関数の戻り値が違う 関数定義として一致しない RETURN
カーソル宣言だけある 本体にカーソル定義がない カーソル名、RETURN

まず確認する順番

PLS-00323は、エラーに出ているサブプログラム名やカーソル名を起点にして、仕様部と本体の一致を確認します。本体側の処理内容ではなく、まず見出しが一致しているかを見るのが近道です。

順番 確認すること ポイント
1 エラー名を確認 PLS-00323 に出ている名前を拾う
2 仕様部の宣言を確認 CREATE PACKAGE 側の見出しを見る
3 本体の定義を確認 CREATE PACKAGE BODY 側に対応定義があるか
4 見出しを比較 名前、引数、型、順番、戻り値、カーソルRETURN型
5 コンパイルエラーを再確認 USER_ERRORS / SHOW ERRORS

比較するときは、処理の中身ではなく、まず見出しだけをそろえます。次の項目のどれか1つでも違うと、Oracleは仕様部の宣言に対応する本体定義として扱えません。

比較ポイント 仕様部 本体 ずれた時の例
名前 export_csv export_csv export_csv_file になっている
引数の数 2個 2個 本体だけ1個少ない
引数の順番 p_id, p_status p_id, p_status 順番が逆
引数の型 employees.hire_date%TYPE 同じ表記 本体だけ DATE
引数モード IN / OUT 同じモード 本体だけ OUT がない
戻り値 RETURN NUMBER RETURN NUMBER 本体だけ RETURN VARCHAR2
カーソル RETURN employees%ROWTYPE 同じRETURN型 本体側のRETURN型が違う

仕様部に宣言したプロシージャを書き忘れたケース

最も分かりやすい原因は、仕様部に宣言したプロシージャを本体に実装していないケースです。仕様部に書いた時点で外部公開の契約になるため、本体側に対応する実装が必要です。

missing-procedure-body.sql
CREATE OR REPLACE PACKAGE pkg_order AS
  PROCEDURE create_order(p_order_id IN NUMBER);
  PROCEDURE close_order(p_order_id IN NUMBER);
END pkg_order;
/

CREATE OR REPLACE PACKAGE BODY pkg_order AS
  PROCEDURE create_order(p_order_id IN NUMBER) AS
  BEGIN
    NULL;
  END create_order;
END pkg_order;
/
-- PLS-00323: close_order is declared in a package specification
-- and must be defined in the package body

修正は、仕様部から不要な宣言を消すか、本体に対応する定義を追加するかのどちらかです。外部から呼ぶ予定があるなら、本体に実装を追加します。

missing-procedure-fixed.sql
CREATE OR REPLACE PACKAGE BODY pkg_order AS
  PROCEDURE create_order(p_order_id IN NUMBER) AS
  BEGIN
    NULL;
  END create_order;

  PROCEDURE close_order(p_order_id IN NUMBER) AS
  BEGIN
    NULL;
  END close_order;
END pkg_order;
/

逆に、パッケージ本体だけにある処理を外部から呼んでいる場合は PLS-00302の原因と解決方法 が近いです。PLS-00323は「仕様部にあるのに本体にない」、PLS-00302は「外部から見える場所にない」と覚えると切り分けやすくなります。

名前が少し違うケース

仕様部と本体で名前が1文字でも違うと、対応する定義とは扱われません。特に英単語の単数形/複数形、略称、アンダースコアの有無で起きやすいです。

name-mismatch.sql
CREATE OR REPLACE PACKAGE pkg_report AS
  PROCEDURE export_csv(p_report_id IN NUMBER);
END pkg_report;
/

CREATE OR REPLACE PACKAGE BODY pkg_report AS
  -- NG: 仕様部はexport_csv、本体はexport_csv_file
  PROCEDURE export_csv_file(p_report_id IN NUMBER) AS
  BEGIN
    NULL;
  END export_csv_file;
END pkg_report;
/

この場合、本体にプロシージャが存在しているように見えても、Oracleから見ると仕様部の export_csv は未実装です。本体側の名前を仕様部に合わせるか、仕様部の宣言を本体に合わせます。

引数の型・順番・%TYPEが一致していないケース

PLS-00323では、仕様部と本体のサブプログラム見出しが対応している必要があります。Oracle公式の例でも、仕様部が employees.hire_date%TYPE、本体が DATE だと、実質的にDATEでも見出し不一致としてエラーになります。

type-mismatch.sql
CREATE OR REPLACE PACKAGE emp_bonus AS
  PROCEDURE calc_bonus(p_hired employees.hire_date%TYPE);
END emp_bonus;
/

CREATE OR REPLACE PACKAGE BODY emp_bonus AS
  -- NG: 実体はDATEでも、仕様部の%TYPEと見出しが一致しない
  PROCEDURE calc_bonus(p_hired DATE) AS
  BEGIN
    NULL;
  END calc_bonus;
END emp_bonus;
/

修正する時は、型の実体が同じかどうかではなく、仕様部と本体の見出しを同じ書き方に寄せます。引数名、順番、IN / OUT / IN OUT、デフォルト値の扱いも合わせて確認します。

type-mismatch-fixed.sql
CREATE OR REPLACE PACKAGE BODY emp_bonus AS
  PROCEDURE calc_bonus(p_hired employees.hire_date%TYPE) AS
  BEGIN
    NULL;
  END calc_bonus;
END emp_bonus;
/

呼び出し時の引数不一致は PLS-00306の原因と解決方法 側の問題です。PLS-00323は、呼び出し前の段階でパッケージ本体が仕様部どおりに定義できていない状態です。

関数のRETURN句が一致していないケース

ファンクションでは、引数だけでなく戻り値の型も見出しの一部です。仕様部と本体で RETURN 句が違うと、対応する定義として扱われません。

function-return-mismatch.sql
CREATE OR REPLACE PACKAGE pkg_price AS
  FUNCTION get_total(p_order_id IN NUMBER) RETURN NUMBER;
END pkg_price;
/

CREATE OR REPLACE PACKAGE BODY pkg_price AS
  -- NG: 仕様部はNUMBER、本体はVARCHAR2
  FUNCTION get_total(p_order_id IN NUMBER) RETURN VARCHAR2 AS
  BEGIN
    RETURN '1000';
  END get_total;
END pkg_price;
/

戻り値の型を変えたい場合は、仕様部と本体の両方を同時に変更します。仕様部を変更すると呼び出し側の再コンパイルや影響確認が必要になるため、公開APIとして扱って慎重に変更します。

カーソル宣言と定義が対応していないケース

パッケージ仕様部ではカーソルの宣言だけを公開し、本体でSELECT文を定義できます。この場合も、仕様部で宣言したカーソルには本体側の定義が必要です。

cursor-definition-missing.sql
CREATE OR REPLACE PACKAGE pkg_emp AS
  CURSOR c_high_salary RETURN employees%ROWTYPE;
END pkg_emp;
/

CREATE OR REPLACE PACKAGE BODY pkg_emp AS
  -- NG: c_high_salaryの定義がない
  PROCEDURE dummy AS
  BEGIN
    NULL;
  END dummy;
END pkg_emp;
/
cursor-definition-fixed.sql
CREATE OR REPLACE PACKAGE BODY pkg_emp AS
  CURSOR c_high_salary RETURN employees%ROWTYPE IS
    SELECT *
    FROM employees
    WHERE salary > 2500;
END pkg_emp;
/

カーソル名だけでなく、RETURN 型の指定も仕様部と本体で合わせます。カーソルの列名・別名が原因で別エラーになる場合は、PLS-00302の観点も確認します。

デフォルト値や引数モードでずれるケース

仕様部と本体で引数のデフォルト値やモードの扱いを変えると、見出しの対応関係を崩す原因になります。公開APIとしては、呼び出し側に見える仕様部の宣言を正とし、本体側は同じ見出しにします。

parameter-mode-default.sql
CREATE OR REPLACE PACKAGE pkg_notify AS
  PROCEDURE send_mail(
    p_user_id IN NUMBER,
    p_force   IN BOOLEAN DEFAULT FALSE
  );
END pkg_notify;
/

CREATE OR REPLACE PACKAGE BODY pkg_notify AS
  -- OKに寄せる: 仕様部と同じ見出しにする
  PROCEDURE send_mail(
    p_user_id IN NUMBER,
    p_force   IN BOOLEAN DEFAULT FALSE
  ) AS
  BEGIN
    NULL;
  END send_mail;
END pkg_notify;
/

INOUTIN OUT の整理は PL/SQLのIN・OUT・IN OUTパラメータ も参考になります。

USER_ERRORSで原因を確認する

PLS-00323はパッケージ本体のコンパイルエラーとして残ります。SQL DeveloperやSQL*Plusのエラー表示だけでなく、USER_ERRORSALL_ERRORS で対象パッケージのエラーを確認します。

user-errors-check.sql
SELECT name,
       type,
       line,
       position,
       text
FROM user_errors
WHERE name = 'PKG_ORDER'
  AND type IN ('PACKAGE', 'PACKAGE BODY')
ORDER BY sequence;

別スキーマのパッケージを調べる場合は ALL_ERRORS を使います。行番号はパッケージ本体側の行を指すため、仕様部の宣言と本体の定義を並べて確認してください。

all-errors-check.sql
SELECT owner,
       name,
       type,
       line,
       position,
       text
FROM all_errors
WHERE owner = 'APP_SCHEMA'
  AND name = 'PKG_ORDER'
ORDER BY sequence;

仕様部と本体を比較するSQL

本番障害やレビュー時は、仕様部と本体のソースを同時に確認できるSQLを用意しておくと便利です。まずエラー名を確認し、その名前で ALL_SOURCE のソースを絞り込みます。

package-source-compare.sql
DEFINE owner_name   = 'APP_SCHEMA'
DEFINE package_name = 'PKG_ORDER'
DEFINE member_name  = 'CLOSE_ORDER'

SELECT type,
       line,
       text
FROM all_source
WHERE owner = UPPER('&owner_name')
  AND name = UPPER('&package_name')
  AND UPPER(text) LIKE '%' || UPPER('&member_name') || '%'
ORDER BY type, line;

ALL_PROCEDURES でサブプログラム名を確認し、ALL_ARGUMENTS で引数を確認する方法もあります。ただしPLS-00323ではコンパイルに失敗している本体側の情報が十分に出ないこともあるため、最終的にはソース比較と ALL_ERRORS を組み合わせます。

procedure-argument-check.sql
SELECT owner,
       object_name AS package_name,
       procedure_name
FROM all_procedures
WHERE owner = 'APP_SCHEMA'
  AND object_name = 'PKG_ORDER'
ORDER BY procedure_name;

SELECT package_name,
       object_name,
       argument_name,
       position,
       data_type,
       in_out
FROM all_arguments
WHERE owner = 'APP_SCHEMA'
  AND package_name = 'PKG_ORDER'
ORDER BY object_name, overload, sequence;

デプロイ時の版ずれで発生するケース

開発環境では通っていたのに本番だけPLS-00323になる場合は、仕様部と本体の反映順序や版ずれを疑います。仕様部だけ新しく、本体が古い、または本体だけ別ブランチの内容になっていると、見出し不一致が起きます。

状況 起きること 確認・対処
仕様部だけ先に反映した 新しい宣言に本体が追いつかない PACKAGE BODYも同じ版で反映する
本体だけ古いファイルを反映した 仕様部にある新引数や新関数が未実装になる デプロイ対象ファイルを確認する
一部オブジェクトだけ再コンパイルした 依存オブジェクトがINVALIDのまま残る USER_OBJECTSUSER_ERRORS を確認
環境差分がある %TYPE参照先やテーブル定義が異なる 本番のテーブル定義・パッケージ定義を確認
deployment-version-check.sql
SELECT object_name,
       object_type,
       status,
       last_ddl_time
FROM user_objects
WHERE object_name = 'PKG_ORDER'
  AND object_type IN ('PACKAGE', 'PACKAGE BODY')
ORDER BY object_type;

SELECT name, type, line, position, text
FROM user_errors
WHERE name = 'PKG_ORDER'
ORDER BY sequence;

PLS-00323と関連エラーの違い

エラー 意味 確認する場所
PLS-00323 仕様部に宣言したものが本体で定義されていない PACKAGE仕様部とPACKAGE BODY
PLS-00302 入れ物の中に対象コンポーネントがない 公開範囲、レコード項目、カーソル列
PLS-00306 呼び出し時の引数の数・型が合わない ALL_ARGUMENTS と呼び出し側
PLS-00905 参照しているオブジェクトが無効 USER_ERRORS
ORA-04063 パッケージ本体やビューにエラーがある 依存オブジェクト、コンパイルエラー
ORA-06508 実行時に呼び出し先プログラム単位を見つけられない INVALID、依存関係、再コンパイル

関連する切り分けとして、PLS-00302PLS-00306PLS-00905ORA-04063ORA-06508 も確認すると、パッケージ系のエラーを整理しやすくなります。

よくある原因と対処一覧

原因 確認すること 対処
本体への実装忘れ 仕様部にある名前が本体にあるか 本体に定義を追加する
名前の不一致 単数/複数、アンダースコア、スペル 仕様部と本体の名前を一致させる
引数型の不一致 %TYPE と実型の書き方 見出しを同じ書き方にする
引数順序の不一致 引数の並び順 仕様部と本体の順番を合わせる
戻り値型の不一致 FUNCTIONの RETURN 戻り値の型を一致させる
カーソル定義なし 仕様部のCURSORに本体定義があるか 本体にカーソルSELECTを定義する
古い本体が残っている デプロイ漏れ、コンパイル順序 仕様部と本体を同じ版で再コンパイル

修正チェックリスト

項目 OKの状態
エラー名を特定した PLS-00323に出ている名前が分かっている
仕様部を確認した 対象の宣言が仕様部にある
本体を確認した 同じ名前の定義がPACKAGE BODYにある
見出しを比較した 名前、引数、型、順番、戻り値が一致している
カーソルを確認した 宣言と定義が対応している
USER_ERRORSを確認した 残っているコンパイルエラーが分かっている
関連エラーを切り分けた PLS-00302やPLS-00306と混同していない

よくある質問

本体に同じ処理を書いているのにPLS-00323になります

名前だけでなく、引数の型、順番、モード、戻り値まで一致しているか確認してください。%TYPE と実型の違いでも見出し不一致になることがあります。

仕様部から宣言を消してもよいですか?

外部から呼ばない内部処理なら削除できます。ただし外部APIとして使われている場合は呼び出し側が壊れるため、本体に実装を追加する方が自然です。

PLS-00302との違いは何ですか?

PLS-00323は仕様部にある宣言を本体が満たしていないエラーです。PLS-00302は、呼び出し側から見た時に対象コンポーネントが見つからないエラーです。

コンパイル順序は関係ありますか?

関係します。仕様部を変更したのに本体が古い、または本体だけ別版を反映した場合に発生しやすくなります。仕様部と本体を同じ版で再コンパイルしてください。

ORA-04063やORA-06508も出ています

PLS-00323でパッケージ本体がINVALIDになり、その後の参照でORA-04063やORA-06508につながることがあります。まず USER_ERRORS でPLS-00323を解消します。

まとめ

PLS-00323は、Oracle PL/SQLのパッケージ仕様部に宣言したプロシージャ、ファンクション、カーソルが、パッケージ本体で対応する形で定義されていない時に発生します。原因は、実装忘れ、名前違い、引数や戻り値の不一致、カーソル定義漏れが中心です。

対応は、USER_ERRORS でエラー名を確認し、仕様部と本体のソースを並べて、対象の見出しを一致させることです。パッケージは仕様部が外部公開の契約、本体が実装なので、公開したものは本体で必ず定義する、という原則で見ると解決しやすくなります。

参考

PLS-00323 – Oracle Database Error Help

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

ALL_ARGUMENTS – Oracle Database Reference