Oracle PL SQL:比较两个存储过程返回的引用游标结果

Posted

技术标签:

【中文标题】Oracle PL SQL:比较两个存储过程返回的引用游标结果【英文标题】:Oracle PL SQL: Comparing ref cursor results returned by two stored procs 【发布时间】: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 的其他列值的长列表。我想检查一下:

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

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

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

【问题讨论】:

【参考方案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 进行比较

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

【讨论】:

以上是关于Oracle PL SQL:比较两个存储过程返回的引用游标结果的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 打印出存储过程返回的引用游标

ORACLE 存储函数

用 Pl/SQL 与 Java 编写的存储过程的性能比较

如何修复存储过程 Oracle PL/SQL 的错误?

PL/SQL轻量版——存储函数/存储过程

oracle存储过程连续执行结果不同