【问题标题】:ora-01422 error by a procedureora-01422 程序错误
【发布时间】:2014-01-10 01:28:04
【问题描述】:

下面的代码抛出 ORA-01422 错误。当我的代码使用select ... into 时,我知道它从表中获取了不止一行,但是如何通过消除 select into 语句来克服这个问题。代码如下:

PROCEDURE Call_Transaction ( Transaction_Name Varchar2, Transaction_Type Varchar2, Form_Open_Type Varchar2 ) IS
BEGIN

Declare
  M_Transaction_Name U_Transaction_Master.Transaction_Name%Type := Upper(Transaction_Name);
  M_Transaction_Cd   U_Transaction_Master.Transaction_Cd%Type;

  T_Transaction_Cd   U_Transaction_Master.Transaction_Cd%Type;
Begin

  Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
  Where Transaction_Name = M_Transaction_Name ;
  Begin
    Select Transaction_Cd Into T_Transaction_Cd From U_User_Wise_Transactions  
    Where Login_Cd = :Global.Login_Cd And Transaction_Cd = M_Transaction_Cd And
    Inst_Cd = :Global.Company_Cd And
    To_Char(Valid_Upto_Date,'DD-MM-YYYY') = '31-12-9999';

    If Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'CALL_FORM' Then
         DECLARE
         id FormModule; 
       BEGIN
         id := Find_Form(M_Transaction_Name); --<Replace your form name>--
         IF Id_Null(id) THEN
             Call_Form(:Global.Forms_Path||M_Transaction_Name||'.Fmx');
         ELSE
             Go_Form(Id) ;
         END IF ;
       END ;       
    Elsif Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'OPEN_FORM' Then
              Open_Form(:Global.Forms_Path||M_Transaction_Name||'.Fmx');
    Elsif Transaction_Type = 'REPORT' And Upper(Form_Open_Type) = 'RUN_PRODUCT' Then
          Declare
            Pl_Id ParamList;
          Begin 
            Pl_Id := Get_Parameter_List('tmpdata'); 
            IF NOT Id_Null(Pl_Id) THEN 
               Destroy_Parameter_List( Pl_Id ); 
            END IF; 
            Pl_Id := Create_Parameter_List('tmpdata'); 

            ADD_Parameter(pl_id,'Inst_Cd',TEXT_PARAMETER,:GLOBAL.Company_Cd);
            ADD_Parameter(pl_id,'Ac_Year_Cd',TEXT_PARAMETER,:GLOBAL.Ac_Year_Cd);
            ADD_Parameter(Pl_Id,'INST_NAME',TEXT_PARAMETER, :Global.Company_name);
            ADD_Parameter(Pl_Id,'ADDRESS',TEXT_PARAMETER, :Global.Address);
            ADD_Parameter(pl_id,'FOOTER',TEXT_PARAMETER,:GLOBAL.Footer);

            Run_Product(REPORTS,:Global.Reports_Path||M_Transaction_Name, SYNCHRONOUS, RUNTIME,
                        FILESYSTEM, Pl_Id, NULL); 
          End;
    End If;

  Exception
    When No_Data_Found Then
         Message('Sorry..., You Do Not Have Authorization For : '||M_Transaction_Cd||' Transaction Code...');
         Raise Form_Trigger_Failure;
  End;

Exception
  When No_Data_Found Then
       Message('The Transaction Cd Not Exists In Transaction Master, Please Contact Administrator...');
       Raise Form_Trigger_Failure;
End;

END;

如何重写代码来解决 ORA-01422 错误?

【问题讨论】:

  • SELECT DISTINCT 可能会这样做。您需要确保查询只返回一行。
  • 我想您应该首先考虑是否希望返回多行。对我来说,我觉得这是一个“糟糕的设计”,而不是一个 SQL 问题。
  • @Joe:这只是一个解决方案,如果所有行都具有相同的值。
  • @PatrickHofman 正确,这就是我说它可能这样做的原因。仅看脚本无法判断。
  • 哪个 SELECT...INTO... 正在抛出 ORA-01422?

标签: oracle plsql


【解决方案1】:

首先,你需要改变异常处理逻辑:

  • 仅包含在begin ... exception ... end 中真正可以的部分 通过异常;
  • 处理 too_many_rows 异常

.

PROCEDURE Call_Transaction ( Transaction_Name Varchar2, Transaction_Type Varchar2, Form_Open_Type Varchar2 ) IS
BEGIN

Declare
  M_Transaction_Name U_Transaction_Master.Transaction_Name%Type := Upper(Transaction_Name);
  M_Transaction_Cd   U_Transaction_Master.Transaction_Cd%Type;

  T_Transaction_Cd   U_Transaction_Master.Transaction_Cd%Type;
Begin

  --  1st select with error analysis
  begin
    Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
    Where Transaction_Name = M_Transaction_Name ;
  exception
    when No_Data_Found then begin
       Message('The Transaction Cd Not Exists In Transaction Master, Please Contact Administrator...');
       Raise Form_Trigger_Failure;
      end;
    when too_many_rows then begin 
       -- What really must be done in this case?
       Message('There are too many Transaction Cd's with passed name In Transaction Master, Please Contact Administrator...');
       Raise Form_Trigger_Failure;
    end;
  end;

  -- 2nd select with error analysis
  begin
    Select Transaction_Cd Into T_Transaction_Cd From U_User_Wise_Transactions  
    Where Login_Cd = :Global.Login_Cd And Transaction_Cd = M_Transaction_Cd And
          Inst_Cd = :Global.Company_Cd And
          To_Char(Valid_Upto_Date,'DD-MM-YYYY') = '31-12-9999';
  Exception
    When No_Data_Found Then begin
         Message('Sorry..., You Do Not Have Authorization For : '||M_Transaction_Cd||' Transaction Code...');
         Raise Form_Trigger_Failure;
      end;
    When too_many_rows Then begin
         -- What really must be done in this case?
         Message('Sorry..., there are some misconfiguration in Authorization Settings For : '||M_Transaction_Cd||' Transaction Code. Please contact Administrator.');
         Raise Form_Trigger_Failure;
      end;
  End;

  If Transaction_Type = 'FORM' And Upper(Form_Open_Type) = 'CALL_FORM' Then

     ---[... all other code skipped ...]---

  End If;


END;

重构后,您需要回答一个问题,即在发现多行的情况下真正必须执行的操作,并根据已实施任务的性质进行处理。

如果您担心可以用来检测值的存在并确定其计数的方法,那么您可以查看this question on StackOverflow

【讨论】:

    【解决方案2】:

    在oracle中,可以保留select into语句,使用ROWNUM限制行数:

    Select Transaction_Cd Into M_Transaction_Cd From U_Transaction_Master
      Where Transaction_Name = M_Transaction_Name 
        and ROWNUM < 2;
    

    【讨论】:

    • @Wernfried 如果您不喜欢该解决方案,您需要提出一条 SQL 语句来保证只选择一行。例如,通过在 where 子句中包含 PK 或应用聚合函数。但是,由于您是唯一知道您的数据的人,因此您也是唯一知道如何做到这一点的人。
    • 如果我不必阅读每条记录而只需要知道是否至少存在一行,我通常会采用以下方法:DECLARE CURSOR curSEL IS SELECT ...; SEL curSEL%ROWTYPE; BEGIN OPEN curSEL; FETCH curSEL into SEL; IF curSEL%FOUND THEN ...;。是的,它需要输入更多代码,但在我看来是更聪明的方法。
    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 2014-04-16
    • 1970-01-01
    • 2021-01-18
    • 2020-04-23
    • 1970-01-01
    • 2018-10-09
    • 2015-09-24
    相关资源
    最近更新 更多