【问题标题】:Oracle PL SQL: Comparing ref cursor results returned by two stored procsOracle PL SQL:比较两个存储过程返回的引用游标结果
【发布时间】:2014-01-29 21:09:49
【问题描述】:

我得到了一个存储过程,它生成一个打开的游标,该游标作为输出传递给报告工具。我重写了这个存储过程来提高性能。我想做的是表明对于给定的一组输入参数,这两个结果集是相同的。

相当于:

select * from CURSOR_NEW
minus
select * from CURSOR_OLD
     union all
select * from CURSOR_OLD
minus
select * from CURSOR_NEW

每个游标从大量表子集中返回几十列。每行都有一个 id 值,以及该 id 的其他列值的长列表。我想检查一下:

  1. 两个游标都返回相同的 id 集(我已经检查过了)
  2. 两个游标的每个 id 都有相同的值列表

如果它只是一两列,我可以将它们连接起来并找到一个哈希值,然后在光标上对其求和。或者另一种方法可能是创建一个父程序,将游标结果插入全局临时表并比较结果。但由于它有几十个专栏,我试图找到一种不那么暴力的方法来进行比较。

此外,如果该解决方案可针对涉及不同游标的其他情况进行扩展,那就太好了,这样就不必每次都手动重写,因为这是我经常遇到的情况。

【问题讨论】:

    标签: sql oracle plsql cursor


    【解决方案1】:

    我想出了一个办法。这比我预期的要复杂得多。我最终使用了一些允许将 REFCURSOR 转换为已定义游标的 DBMS_SQL 过程。 Oracle 在这里有相关文档: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/dynamic.htm#LNPLS00001

    之后,我将行值连接成一个字符串并打印散列。对于更大的游标,我将更改 concat_col_vals 以使用 CLOB 来防止它溢出。

    p_testCursors 返回一个简单的 refcursor 用于示例目的。

    declare
      cx_1              sys_refcursor;
      c                 NUMBER;
      desctab           DBMS_SQL.DESC_TAB;
      colcnt            NUMBER;
      stringvar         VARCHAR2(4000);
      numvar            NUMBER;
      datevar           DATE;
      concat_col_vals   varchar2(4000);
      col_hash          number;
      h                 raw(32767);
      n                 number;
    
    BEGIN
      p_testCursors(cx_1);
    
      c := DBMS_SQL.TO_CURSOR_NUMBER(cx_1);
      DBMS_SQL.DESCRIBE_COLUMNS(c, colcnt, desctab);
    
      -- Define columns:
      FOR i IN 1 .. colcnt LOOP
        IF desctab(i).col_type = 2 THEN
          DBMS_SQL.DEFINE_COLUMN(c, i, numvar);
        ELSIF desctab(i).col_type = 12 THEN
          DBMS_SQL.DEFINE_COLUMN(c, i, datevar);
          -- statements
        ELSE
          DBMS_SQL.DEFINE_COLUMN(c, i, stringvar, 4000);
        END IF;
      END LOOP;
    
      -- Fetch rows with DBMS_SQL package:
      WHILE DBMS_SQL.FETCH_ROWS(c) > 0 LOOP
      concat_col_vals := '~';
        FOR i IN 1 .. colcnt LOOP
          IF (desctab(i).col_type = 1) THEN
            DBMS_SQL.COLUMN_VALUE(c, i, stringvar);
            --Dbms_Output.Put_Line(stringvar);
            concat_col_vals := concat_col_vals || '~' || stringvar;
          ELSIF (desctab(i).col_type = 2) THEN
            DBMS_SQL.COLUMN_VALUE(c, i, numvar);
            --Dbms_Output.Put_Line(numvar);
            concat_col_vals := concat_col_vals || '~' || to_char(numvar);
          ELSIF (desctab(i).col_type = 12) THEN
            DBMS_SQL.COLUMN_VALUE(c, i, datevar);
            --Dbms_Output.Put_Line(datevar);
            concat_col_vals := concat_col_vals || '~' || to_char(datevar);
            -- statements
          END IF;
        END LOOP;
        DBMS_OUTPUT.PUT_LINE(concat_col_vals);
        col_hash :=  DBMS_UTILITY.GET_SQL_HASH(concat_col_vals, h, n);
        DBMS_OUTPUT.PUT_LINE('Return Value: ' || TO_CHAR(col_hash));
        DBMS_OUTPUT.PUT_LINE('Hash: ' || h);
      END LOOP;
    
      DBMS_SQL.CLOSE_CURSOR(c);
    
    END;
    /
    

    【讨论】:

      【解决方案2】:

      这对 Oracle 来说并非易事。

      非常好的文章您可以在 dba-oracle 网站上找到:
      Sql patterns symmetric diff
      Convert set to join sql parameter

      如果您经常需要,您可以:

      • 添加“哈希列”并始终使用触发器插入填充它,或者
      • 为游标输出中的每个表获取唯一值(创建唯一索引)并仅将此列与 anijoin 进行比较

      您可以在文章中找到其他可能性。

      【讨论】:

        猜你喜欢
        • 2011-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-18
        • 1970-01-01
        • 2022-11-05
        • 2023-03-05
        相关资源
        最近更新 更多