Arrays 使用关联数组捕获错误列表?

Arrays 使用关联数组捕获错误列表?,arrays,oracle,plsql,associative,Arrays,Oracle,Plsql,Associative,我正在编写一个返回IN-OUT游标的过程。我想检查IN参数是否正确。如果不是,光标将在结果的同一位置接收错误网格,这些错误仅在参数中发生。光标是crystal报表的IN参数 CREATE OR REPLACE PROCEDURE test_err ( p_hire_date DATE, P_last_name IN VARCHAR2, refcur IN OUT SYS_REFCURSOR) IS

我正在编写一个返回IN-OUT游标的过程。我想检查IN参数是否正确。如果不是,光标将在结果的同一位置接收错误网格,这些错误仅在参数中发生。光标是crystal报表的IN参数

CREATE OR REPLACE PROCEDURE test_err (
                p_hire_date DATE,
                P_last_name IN VARCHAR2,
                refcur IN OUT SYS_REFCURSOR)
IS
   err_num1   PLS_INTEGER DEFAULT 0;
   err_msg1   VARCHAR2 (150);
   err_num2   PLS_INTEGER DEFAULT 0;
   err_msg2   VARCHAR2 (150);
   v_hire_date DATE;
   v_last_name VARCHAR2 (150);

BEGIN
   v_hire_date :=to_date(p_hire_date , 'dd/mm/yyyy');
   v_last_name :=UPPER(TRIM (p_last_name));
    --
   IF v_hire_date < '23/01/1990'
   THEN
      BEGIN
         err_num1 := 1;
         err_msg1 := 'Try another later hire date';
      END;
   ELSE  NULL;
   END IF;
   --
   IF v_last_name IS NULL
   THEN
      BEGIN
         err_num2 := 2;
         err_msg2 := 'Please input employee''s last name';
      END;
   ELSE v_last_name := '%'||v_last_name||'%';
   END IF;
   --
   IF (err_num1 = 0 AND err_num2 = 0)
   THEN
      GOTO main_task;
   ELSE
      GOTO err_hdle;
   END IF;

  <<main_task>>
   OPEN refcur FOR
      SELECT employee_id, last_name, hire_date
        FROM hr.employees
       WHERE hire_date >= v_hire_date
         AND UPPER(last_name) LIKE v_last_name;

   GOTO end_task;

  <<err_hdle>>
   OPEN refcur FOR
      SELECT 'Error '||err_num1 AS employee_id, err_msg1 AS last_name,
              v_hire_date AS hire_date
        FROM DUAL
       WHERE err_num1 <> 0

      UNION ALL
      SELECT 'Error '||err_num2 AS employee_id, err_msg2 AS last_name,
              v_hire_date AS hire_date
        FROM DUAL
       WHERE err_num2 <> 0;

  <<end_task>>
   NULL;
END;

然而,我不得不承认这段代码看起来很傻。我想知道有没有办法使用关联数组来捕获错误,然后将它们提取到游标中,而不是扩展err_num3,err_msg3,。。。err_num_n、err_msg_n,并在err_hdle块中连续使用UNION ALL。请帮我弄清楚这个案子。谢谢大家!

首先是一些一般性意见:

租用日期:=截止日期租用日期'dd/mm/yyyy';你为什么要把约会变成约会

如果你只是想削减时间价值,你可以这样做 v_雇佣日期:=TRUNCp_雇佣日期

如果v_hire_date<'23/01/1990',那么为什么要将日期值与字符串值进行比较? 最好将日期值与日期进行比较,例如

如果v_雇佣日期<日期'1990 01-23',则或

如果租赁日期<至日期'1990年1月23日','dd/mm/yyyy',则

那么您不应该在代码中使用GOTO,这通常被认为是糟糕的编程风格

现在,对于您的实际问题,您可以为错误消息定义一个嵌套表

CREATE OR REPLACE TYPE err_msg_table_type AS TABLE OF hr.employees%ROWTYPE;

CREATE OR REPLACE PROCEDURE test_err (
                p_hire_date DATE,
                P_last_name IN VARCHAR2,
                refcur IN OUT SYS_REFCURSOR)
IS
   v_hire_date DATE;
   v_last_name VARCHAR2 (150);
   v_err_msg err_msg_table_type := err_msg_table_type();

BEGIN
   v_hire_date := TRUNC(p_hire_date);
   v_last_name :=UPPER(TRIM(p_last_name));

   IF v_hire_date < DATE '1990-01-23' THEN
      v_err_msg.EXTEND;
      v_err_msg(v_err_msg.LAST).employee_id := 1;
      v_err_msg(v_err_msg.LAST).last_name := 'Try another later hire date'; 
   END IF;

   IF v_last_name IS NULL THEN
      v_err_msg.EXTEND;
      v_err_msg(v_err_msg.LAST).employee_id := 2;
      v_err_msg(v_err_msg.LAST).last_name := 'Please input employee''s last name'; 
   END IF;

   IF v_err_msg.COUNT = 0 THEN
      OPEN refcur FOR
      SELECT employee_id, last_name, hire_date
        FROM hr.employees
       WHERE hire_date >= v_hire_date
         AND UPPER(last_name) LIKE '%'||v_last_name||'%';
   ELSE
      OPEN refcur FOR
      SELECT * FROM TABLE(v_err_msg);
    END IF;      
END;

感谢@Wernfried,我找到了适合我的案例的答案。我应该按照@Wernfried的建议部署嵌套表,而不是使用关联数组。但是,我必须首先创建新的用户定义对象。在@Wernfried的脚本中,他基于%ROWTYPE定义了一个新的类型err_msg_table_type,该类型只能在PL/SQL引擎中工作,或者只适用于包体。要在SQL引擎中创建新类型,我们需要明确每个内部组件的数据类型,就像创建新表的方式一样

--Create table
CREATE TABLE hr.employees
(
   employee_id      NUMBER (6, 0),
   first_name       VARCHAR2 (20 BYTE),
   last_name        VARCHAR2 (25 BYTE),
   hire_date        DATE
);

--Insert records
INSERT INTO employees 
VALUES(100,'King','Steven',TO_DATE('2003/06/17', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(101,'Kochhar','Neena',TO_DATE('2005/09/21', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(102,'De Haan','Lex',TO_DATE('2001/01/13', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(103,'Hunold','Alexander',TO_DATE('2006/01/03', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(104,'Ernst','Bruce',TO_DATE('2007/05/21', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(105,'Austin','David',TO_DATE('2005/06/25', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(106,'Pataballa','Valli',TO_DATE('2006/02/05', 'YYYY/MM/DD'));
INSERT INTO employees 
VALUES(107,'Lorentz','Diana',TO_DATE('2007/02/07', 'YYYY/MM/DD'));
COMMIT;

--Create Objects
CREATE OR REPLACE TYPE error_set AS OBJECT (
   error_id      NUMBER (6, 0),
   error_note    VARCHAR2 (150 BYTE),
   hint_note      VARCHAR2 (150 BYTE)
   );

CREATE OR REPLACE TYPE err_msg_tab_type IS TABLE OF error_set;

--Create procedure
CREATE OR REPLACE PROCEDURE test_err (
                p_hire_date DATE,
                P_last_name IN VARCHAR2,
                refcur IN OUT SYS_REFCURSOR)

IS
   v_hire_date  DATE;
   v_last_name  VARCHAR2 (150);
   v_err_msg    err_msg_tab_type := err_msg_tab_type();

BEGIN
   v_hire_date := TO_DATE(p_hire_date, 'dd/mm/yyyy');
   v_last_name := UPPER(TRIM(p_last_name));

   IF TRIM(p_hire_date) IS NULL THEN
   v_err_msg.EXTEND;
   v_err_msg(v_err_msg.LAST):= error_set (1,
                                         'Please input the hire date',
                                         NULL); 
   END IF;

   IF v_hire_date < to_date ('13/01/2001','dd/mm/yyyy') THEN
      v_err_msg.EXTEND;
      v_err_msg(v_err_msg.LAST):= error_set (2,
                                             'Try another later hire date',
                                             'The oldest hire date was Jan 13, 2001'); 
   END IF;

   IF v_last_name IS NULL THEN
      v_err_msg.EXTEND;  
      v_err_msg(v_err_msg.LAST):= error_set (3,
                                             'Please input employee''s last name',
                                             NULL); 
   END IF;

   IF v_err_msg.COUNT = 0 THEN
      OPEN refcur FOR
      SELECT employee_id, last_name, first_name, hire_date
        FROM hr.employees
       WHERE hire_date >= v_hire_date
         AND UPPER(last_name) LIKE '%'||v_last_name||'%';
   ELSE
      OPEN refcur FOR
      SELECT error_id AS employee_id, error_note AS last_name,
             hint_note AS first_name, v_hire_date AS hire_date
        FROM TABLE(v_err_msg);
    END IF;      
END;

/*
--CLEAN UP after testing
DROP PROCEDURE test_err;
DROP TYPE err_msg_tab_type;
DROP TYPE error_set;
*/

谢谢您的帮助,但我在编译您建议的过程时遇到了以下错误:-错误:检查编译器日志[35/27]PLS-00642:SQL语句中不允许使用本地集合类型;|[35/21]PL/SQL:ORA-22905:无法从非嵌套表项访问行;|[35/7]PL/SQL:SQL语句忽略实际上,我们不能将TABLE函数用于关联数组,因为[SELECT*FROM TABLE v_err_msg]必须由SQL引擎编译,而关联数组仅用于PL/SQL引擎。PL/SQL将SELECT语句传递给SQL引擎单独执行,然后从SQL引擎接收结果。@gngdoan,我修改了关于本地集合类型的回答。非常感谢,@Wernfried。我真的很感谢你的支持,帮助我找到了这个案子。顺便说一下,如果您尝试基于%ROWTYPE创建新的用户定义日期类型,您将收到以下消息“[PLS-00329]:架构级别类型非法引用员工”。顺致敬意,