【问题标题】:Validation of result before executing the query在执行查询之前验证结果
【发布时间】:2019-05-27 03:43:50
【问题描述】:

在运行存储过程以获取一些行时,首先我想验证查询是否会在发送结果之前返回一行,其次是否可以在不运行相同查询两次的情况下进行验证。

我正在使用游标来存储产生的结果,所以我尝试了游标属性%ROWCOUNT & %NOTFOUND。但这并不完全奏效。另外,我想这样做而不在光标上运行循环。

procedure MODULE_LIST_GK(p_module_Id IN MODULE_LIST.MODULE_ID% TYPE,
                                     p_Error_Code     out nvarchar2,
                                     p_Error_Msg      out nvarchar2,
                                     p_Cursor         out sys_refcursor)   IS
 BEGIN
    OPEN p_Cursor FOR
       SELECT A.MODULE_ID,
         A.MODULE_NM,
         A.AUTH_STATUS_ID
         FROM MODULE_LIST A
         WHERE A.MODULE_ID=p_module_Id;

       SELECT COUNT(MODULE_ID)
       INTO v_row_num
        FROM MODULE_LIST A
        WHERE A.MODULE_ID=p_module_Id;

       IF v_row_num=0 THEN
         p_Error_Code := SQLCODE;
         p_Error_Msg := 'Does not Exists';
         Return;
       end IF;
    EXCEPTION
      WHEN OTHERS THEN
        p_error_code:= SQLCODE;
        p_error_msg := SQLERRM;
  END MODULE_LIST_GK;

【问题讨论】:

    标签: oracle stored-procedures plsql


    【解决方案1】:

    您的实现有几点可以改进。

    首先如果你期望很多参数返回的光标是空的, 比首先检查空光标并且只有在检查之后才打开光标。反之亦然。

    如何判断光标是否为空?不幸的是,您必须获取第一行才能验证它。

     open l_cur for
       select id, status from tab where id = p_id; 
     fetch l_cur into l_id, l_status;
     if l_cur%NOTFOUND then
        p_Error_Msg := 'Does not Exists';
        Return;
     end if;
    

    这种检查比经常使用的count(*) 更有效,因为它只考虑前(几)行而不计算游标中的所有行

    如果检查失败,你就准备好了,否则简单地打开光标并返回它。

     open  l_cur for
       select id, status from tab where id = p_id; 
       p_Cursor := l_cur;
    

    我想到了另外两个想法。

    如果数据库非常动态,您应该重新考虑通用方法。 当其他会话删除检查和第二次打开游标之间的某些行时,您将如何处理?

    最后考虑返回异常而不是返回码。

    【讨论】:

    • 所以无论哪种方式我都必须查询一次才能找到 1. 如果有数据? 2.如果有数据获取它们。我只是好奇是否有什么方法可以通过查询一次来实现?
    • @radioactive001 我想您可以将结果复制到临时表中。然而,调用者决定如何处理空结果集可能更有意义。
    • AFAIK 你必须从游标中获取第一行才能检查它是否为空。无论如何,您可以通过稍微更改接口的合同来摆脱它。现在客户端必须测试error_msg,如果它为空,则处理游标。您可以将其更改为从游标中获取第一行,如果没有行则将其作为错误处理,否则处理游标。
    【解决方案2】:

    为了知道游标是否包含行,您必须打开它并获取第一行。完成此操作后,返回该游标就没有意义了,因为接收者将无法获取第一行,因为游标已经指向它之外。

    因此,您必须选择两次。您要做的是在此处使用ROWNUMEXISTS 子句向DBMS 显示您对更多行不感兴趣。这样可以极大地加快查询速度。

    PROCEDURE module_list_gk(p_module_id   IN   MODULE_LIST.MODULE_ID%TYPE,
                             p_error_code  OUT  NVARCHAR2,
                             p_error_msg   OUT  NVARCHAR2,
                             p_cursor      OUT  SYS_REFCURSOR) IS
      v_count INTEGER;
    BEGIN
      SELECT COUNT(*)
      INTO v_count
      FROM module_list
      WHERE module_id = p_module_Id
      AND ROWNUM = 1;
    
      IF v_count = 0 THEN
        p_error_code := 0; -- Or -1403 for NO DATA FOUND if you like
        p_error_msg := 'Does not Exists';
        RETURN;
      END IF;
    
      OPEN p_Cursor FOR
        SELECT module_id, module_nm, auth_status_id
        FROM module_list
        WHERE module_id = p_module_id;
    
    EXCEPTION WHEN OTHERS THEN
      p_error_code:= SQLCODE;
      p_error_msg := SQLERRM;
    END module_list_gk;
    

    顺便说一下,第一个查询的 SQLCODE 将为 0(SELECT COUNT(*) 返回一行,其中包含找到的记录数 - 因此没有错误)。这就是为什么您应该决定显式返回零或一些错误代码,例如 -1403。

    这里和EXISTS一样:

    BEGIN
      SELECT CASE WHEN EXISTS
      (
        SELECT NULL
        FROM module_list
        WHERE module_id = p_module_Id
      ) THEN 1 ELSE 0 END
      INTO v_count
      FROM DUAL;
    
      IF v_count = 0 THEN
    

    【讨论】:

      猜你喜欢
      • 2011-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      相关资源
      最近更新 更多