Oracle PL / SQL - Benutzerdefinierte Ausnahme mit benutzerdefiniertem SQLERRM auslösen

78

Ist es möglich, benutzerdefinierte Ausnahmen zu erstellen und das SQLERRM zu ändern?

Zum Beispiel:

DECLARE
    ex_custom       EXCEPTION;
BEGIN
    RAISE ex_custom;
EXCEPTION
    WHEN ex_custom THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

Die Ausgabe lautet "Benutzerdefinierte Ausnahme". Ist es möglich, diese Nachricht zu ändern?

EDIT: Hier sind einige Details.

Ich hoffe, dieser zeigt, was ich besser machen will.

DECLARE
    l_table_status      VARCHAR2(8);
    l_index_status      VARCHAR2(8);
    l_table_name        VARCHAR2(30) := 'TEST';
    l_index_name        VARCHAR2(30) := 'IDX_TEST';
    ex_no_metadata      EXCEPTION;
BEGIN

    BEGIN
        SELECT  STATUS
        INTO    l_table_status
        FROM    USER_TABLES
        WHERE   TABLE_NAME      = l_table_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Table metadata does not exist."
            RAISE ex_no_metadata;
    END;

    BEGIN
        SELECT  STATUS
        INTO    l_index_status
        FROM    USER_INDEXES
        WHERE   INDEX_NAME      = l_index_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Index metadata does not exist."
            RAISE ex_no_metadata;
    END;

EXCEPTION
    WHEN ex_no_metadata THEN
        DBMS_OUTPUT.PUT_LINE('Exception will be handled by handle_no_metadata_exception(SQLERRM) procedure here.');
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

In Wirklichkeit gibt es Dutzende dieser Unterblöcke. Ich frage mich, ob es eine Möglichkeit gibt, eine einzelne benutzerdefinierte Ausnahme für jeden dieser Unterblöcke auszulösen, aber eine andere Meldung zu geben, anstatt für jeden Unterblock eine separate benutzerdefinierte Ausnahme zu erstellen.

In .NET wäre es so, als hätte man eine benutzerdefinierte Ausnahme wie diese:

    public class ColorException : Exception
    {
        public ColorException(string message)
            : base(message)
        {
        }
    }

Und dann hätte eine Methode ungefähr so:

        if (isRed)
        {
            throw new ColorException("Red is not allowed!");
        }

        if (isBlack)
        {
            throw new ColorException("Black is not allowed!");
        }

        if (isBlue)
        {
            throw new ColorException("Blue is not allowed!");
        }
tgxiii
quelle

Antworten:

149

Ja. Sie müssen nur die RAISE_APPLICATION_ERRORFunktion verwenden. Wenn Sie Ihre Ausnahme auch benennen möchten, müssen Sie das EXCEPTION_INITPragma verwenden, um die Fehlernummer der benannten Ausnahme zuzuordnen. Etwas wie

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    ex_custom EXCEPTION;
  3    PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
  4  begin
  5    raise_application_error( -20001, 'This is a custom error' );
  6  exception
  7    when ex_custom
  8    then
  9      dbms_output.put_line( sqlerrm );
 10* end;
SQL> /
ORA-20001: This is a custom error

PL/SQL procedure successfully completed.
Justin Cave
quelle
1
Genau das, was ich brauche! Ich glaube, ich habe meine Bearbeitung vorgenommen, während Sie meine Frage bereits beantwortet haben. Vielen Dank.
tgxiii
38

Sie können RAISE_APPLICATION_ERROR folgendermaßen verwenden:

DECLARE
    ex_custom       EXCEPTION;
BEGIN
    RAISE ex_custom;
EXCEPTION
    WHEN ex_custom THEN
        RAISE_APPLICATION_ERROR(-20001,'My exception was raised');
END;
/

Das wird eine Ausnahme auslösen, die aussieht wie:

ORA-20001: My exception was raised

Die Fehlernummer kann zwischen -20001 und -20999 liegen.

Tony Andrews
quelle
21

Normalerweise verliere ich den Überblick über alle -20001Fehlercodes meines Typs, daher versuche ich, alle meine Anwendungsfehler in einem netten Paket wie dem folgenden zu konsolidieren:

SET SERVEROUTPUT ON

CREATE OR REPLACE PACKAGE errors AS
  invalid_foo_err EXCEPTION;
  invalid_foo_num NUMBER := -20123;
  invalid_foo_msg VARCHAR2(32767) := 'Invalid Foo!';
  PRAGMA EXCEPTION_INIT(invalid_foo_err, -20123);  -- can't use var >:O

  illegal_bar_err EXCEPTION;
  illegal_bar_num NUMBER := -20156;
  illegal_bar_msg VARCHAR2(32767) := 'Illegal Bar!';
  PRAGMA EXCEPTION_INIT(illegal_bar_err, -20156);  -- can't use var >:O

  PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL);
END;
/

CREATE OR REPLACE PACKAGE BODY errors AS
  unknown_err EXCEPTION;
  unknown_num NUMBER := -20001;
  unknown_msg VARCHAR2(32767) := 'Unknown Error Specified!';

  PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL) AS
    v_msg VARCHAR2(32767);
  BEGIN
    IF p_err = unknown_num THEN
      v_msg := unknown_msg;
    ELSIF p_err = invalid_foo_num THEN
      v_msg := invalid_foo_msg;
    ELSIF p_err = illegal_bar_num THEN
      v_msg := illegal_bar_msg;
    ELSE
      raise_err(unknown_num, 'USR' || p_err || ': ' || p_msg);
    END IF;

    IF p_msg IS NOT NULL THEN
      v_msg := v_msg || ' - '||p_msg;
    END IF;

    RAISE_APPLICATION_ERROR(p_err, v_msg);
  END;
END;
/

Rufen Sie dann errors.raise_err(errors.invalid_foo_num, 'optional extra text')an, um es wie folgt zu verwenden:

BEGIN
  BEGIN
    errors.raise_err(errors.invalid_foo_num, 'Insufficient Foo-age!');
  EXCEPTION
    WHEN errors.invalid_foo_err THEN
      dbms_output.put_line(SQLERRM);
  END;

  BEGIN
    errors.raise_err(errors.illegal_bar_num, 'Insufficient Bar-age!');
  EXCEPTION
    WHEN errors.illegal_bar_err THEN
      dbms_output.put_line(SQLERRM);
  END;

  BEGIN
    errors.raise_err(-10000, 'This Doesn''t Exist!!');
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(SQLERRM);
  END;
END;
/

erzeugt diese Ausgabe:

ORA-20123: Invalid Foo! - Insufficient Foo-age!
ORA-20156: Illegal Bar! - Insufficient Bar-age!
ORA-20001: Unknown Error Specified! - USR-10000: This Doesn't Exist!!
MassuguGo
quelle
1
Schöner Tipp! Es war sehr nützlich für mein Projekt!
SnakeSheet
1
Dies ist eine gute Praxis. Zwei kleinere Probleme beim Erhöhen von_application_error : 1) Die Größe des 2. Parameters ist auf 2048 Byte begrenzt und 2) Ich würde es vorziehen, wenn der 3. Parameter true(anstelle der Standardeinstellung false) ist, um eine vollständige Stapelverfolgung zu erhalten.
user272735
5
declare
   z exception;

begin
   if to_char(sysdate,'day')='sunday' then
     raise z;
   end if;

   exception 
     when z then
        dbms_output.put_line('to day is sunday');
end;
Nagaraju Nampally
quelle
2
create or replace PROCEDURE PROC_USER_EXP 
AS
duplicate_exp EXCEPTION;
PRAGMA EXCEPTION_INIT( duplicate_exp, -20001 );
LVCOUNT NUMBER;
BEGIN
  SELECT COUNT(*) INTO LVCOUNT FROM JOBS WHERE JOB_TITLE='President';
  IF LVCOUNT >1 THEN 
   raise_application_error( -20001, 'Duplicate president customer excetpion' );
  END IF;

  EXCEPTION 
  WHEN duplicate_exp THEN 
  DBMS_OUTPUT.PUT_LINE(sqlerrm);
END PROC_USER_EXP;

Die Ausgabe von ORACLE 11g sieht folgendermaßen aus:

Connecting to the database HR. 
ORA-20001: Duplicate president customer excetpion 
Process exited. 
Disconnecting from the database HR
Raj Sharma
quelle
Die Ausgabe von ORACLE 11g sieht folgendermaßen aus: - Verbindung zur Datenbank HR herstellen. ORA-20001: Doppelter Präsident-Kunden-Exzessprozess beendet. Verbindung zur Datenbank HR trennen.
Raj Sharma
Dies ist gutes Zeug, außer (ironischerweise) für den EXCEPTION-Block. In fast allen Fällen sollten Ausnahmen, die in einer WHEN-Klausel behandelt werden, erneut für das aufrufende Programm ausgelöst werden. Das aufrufende Programm muss wissen, dass die aufgerufene Routine fehlgeschlagen ist. Die einfache Verwendung von DBMS_OUTPUT ist nicht gut genug, da das aufrufende Programm den Serverausgabepuffer möglicherweise nicht verarbeiten kann und / oder das aufrufende Programm den Fehler nicht zur Bestätigung zwingt.
APC