从 PLSQL 中所有动态找到的表中获取数据时遇到问题

Posted

技术标签:

【中文标题】从 PLSQL 中所有动态找到的表中获取数据时遇到问题【英文标题】:Trouble in fetching the data from all the dynamically found tables in PLSQL 【发布时间】:2016-04-05 13:46:27 【问题描述】:

这是我在使用 Oracle PLSQL 进行编程以完成我的一项严肃任务时遇到的主要脑痛问题之一。

我需要编写一个过程/函数/匿名块来获取未知表中的所有记录,比如 MyUnknownTable。这个未知的表名可以是我现有数据库中的任何表。问题是,使用一些决策逻辑,我动态地找到一个表名,然后传递给下面的代码(意味着 MyUnknownTable 将是我将在其他代码中根据我的逻辑决定的任何表名),然后是下面的代码应该在输出中从该表中获取所有记录。

SET serveroutput ON;
DECLARE
  rc_ SYS_REFCURSOR;
  c_         NUMBER;
  i_         NUMBER;
  col_count_ NUMBER;
  desc_tab_ DBMS_SQL.DESC_TAB;
  table_header VARCHAR2(2000);
  table_data   VARCHAR2(2000);

  l_rec **MyUnknownTable**%rowtype;

BEGIN
  OPEN rc_ FOR 'SELECT * FROM **MyUnknownTable**';

  LOOP
     FETCH rc_ INTO l_rec;
     EXIT WHEN l_rec%notfound;
     dbms_output.put_line(**<All Columns from l_rec>**);
  END LOOP;

  rc_.CLOSE();
END;

上面代码的问题是l_rec。 我知道这种方式是错误且毫无意义的,但在上面的代码中,我将l_rec 定义为**MyUnknownTable**%rowtype,这样l_rec 将获得与MyUknownTable 相同的数据结构,FETCH rc_ INTO l_rec 将起作用。我还没有找到如何动态声明l_rec 以便它可以与我的代码将传递给此代码块的任何未知表一起使用的方法。

我将我的大脑使用到了它的极限减半,但无法进入“如何”的方式。

我怀着很大的希望需要宇宙其他人的帮助。

Oracle 支持社区 https://community.oracle.com/thread/1036107 甚至没有回答这个问题

以下是我需要编写代码以获取数据的原始代码供参考:

SET serveroutput ON;
DECLARE
  v_column_name  VARCHAR2(100) := UPPER('CASE_ID');
  v_column_value VARCHAR2(100) := '324735';
  CURSOR table_names_cur
  IS
    SELECT DISTINCT table_name
    FROM ALL_TAB_COLS
    WHERE UPPER(column_name)=UPPER(v_column_name)
    AND table_name NOT LIKE '%$%'
    AND owner LIKE 'ARGUS_APP';

  rc_ SYS_REFCURSOR;
  c_         NUMBER;
  i_         NUMBER;
  col_count_ NUMBER;
  desc_tab_ DBMS_SQL.DESC_TAB;
  table_header VARCHAR2(2000);
  table_data   VARCHAR2(2000);
BEGIN
  dbms_output.put_line ('Script Execution Started ..');
  FOR c_table_name IN table_names_cur
  LOOP
    OPEN rc_ FOR 'SELECT * FROM ' || c_table_name.table_name || ' WHERE ' || v_column_name || ' = ' || v_column_value || '';
    c_ := DBMS_SQL.to_cursor_number(rc_);
    DBMS_SQL.DESCRIBE_COLUMNS(c_, col_count_, desc_tab_);
    table_header:= 'XYZ';
    FOR i_      IN 1..col_count_
    LOOP
      IF table_header = 'XYZ' THEN
        table_header := desc_tab_(i_).col_name;
      ELSE
        table_header := table_header || '|' || desc_tab_(i_).col_name;
      END IF;
    END LOOP;
    DBMS_OUTPUT.PUT_LINE(table_header);
    /*
    **Need to write code here to fetch data from rc_**
    */
    DBMS_SQL.CLOSE_CURSOR(c_);
  END LOOP;
  dbms_output.put_line ('Script Execution Completed ..' );
END;

~阿吉特·辛格·萨坎

【问题讨论】:

我认为您不能这样声明记录。您将需要dbms_sql.describe_columns 来获取结果的列,然后使用该信息 您真正需要对这个未知表中的数据做什么?这将推动代码的复杂程度。在此示例中,您似乎已经知道该表有一个名为 CASE_ID 的列,并且这是您唯一感兴趣的列。如果实际情况如此,那么问题比您更容易试图让它充满活力。 这种动态 sql 对于 CRUD 应用程序来说性能不佳。您是否考虑在日常使用的应用程序中使用它? 您确定要使用dbms_output,它依赖于启用了该会话的会话,并且不能被其他任何东西使用?也许您想返回/显示参考光标? This 有一个简单的 describe_columns 方法来转储现有的 ref 游标,那么这是在做你需要的事情吗? 您可以使用包DBMS_SQL 完成所有这些操作。然而,告诉我们你真正想要达到的目标(而不是你假设的工作),那么你会得到更好的答案。也许为一张真实的桌子举一个完整的例子。 【参考方案1】:

最后是适合我的代码:

SET serveroutput ON;
DECLARE
  v_column_name  VARCHAR2(100) := UPPER('CASE_ID');
  v_column_value VARCHAR2(100) := '324735';
  CURSOR table_names_cur
  IS
    SELECT DISTINCT table_name
    FROM ALL_TAB_COLS
    WHERE UPPER(column_name)=UPPER(v_column_name)
    AND table_name NOT LIKE '%$%'
    AND owner LIKE 'ARGUS_APP';
  --AND table_name IN ('CASE_MASTER');
  rc_ SYS_REFCURSOR;
  c_         NUMBER;
  i_         NUMBER;
  col_count_ NUMBER;
  desc_tab_ DBMS_SQL.DESC_TAB;
  table_header VARCHAR2(2000);
  table_data   VARCHAR2(2000);
  header_done  NUMBER;
  L_VARCHAR    VARCHAR2(4000);
  --filehandle1 UTL_FILE.FILE_TYPE;
  -- l_rec case_master%rowtype;
BEGIN
  --filehandle1 := UTL_FILE.FOPEN ('C:\Users\M513705\Desktop\Ajit\','test.txt','W');
  -- UTL_FILE.PUT (filehandle1,'Works');
  dbms_output.put_line ('Script Execution Started ..');
  FOR c_table_name IN table_names_cur
  LOOP

    header_done:=0;

    OPEN rc_ FOR 'SELECT * FROM ' || c_table_name.table_name || ' WHERE ' || v_column_name || ' = ' || v_column_value || '';
    c_ := DBMS_SQL.to_cursor_number(rc_);
    DBMS_SQL.DESCRIBE_COLUMNS(c_, col_count_, desc_tab_);

    FOR i       IN 1..col_count_
    LOOP
      DBMS_SQL.DEFINE_COLUMN(c_, i, L_VARCHAR, 4000);
    END LOOP;

    table_header:= 'XYZ';

    LOOP
      EXIT WHEN DBMS_SQL.FETCH_ROWS (c_) = 0;

      IF header_done              = 0 THEN
        header_done              := 1;
        FOR i_                   IN 1..col_count_
        LOOP
          IF table_header = 'XYZ' THEN
            table_header := desc_tab_(i_).col_name;
          ELSE
            table_header := table_header || '|' || desc_tab_(i_).col_name;
          END IF;
        END LOOP;
        DBMS_OUTPUT.PUT_LINE('Table: ' || c_table_name.table_name);
        DBMS_OUTPUT.PUT_LINE(table_header);
      END IF;

      table_data:='XYZ';

      FOR i     IN 1..col_count_
      LOOP
        DBMS_SQL.COLUMN_VALUE(c_, i, L_VARCHAR);
        -- DBMS_OUTPUT.PUT_LINE('Row ' || DBMS_SQL.LAST_ROW_COUNT || ': ' || desc_tab_(i).col_name || ' = ' || L_VARCHAR);
        IF table_data = 'XYZ' THEN
          table_data := L_VARCHAR;
        ELSE
          table_data := table_data || '|' || L_VARCHAR;
        END IF;
      END LOOP;

      DBMS_OUTPUT.PUT_LINE(table_data);

    END LOOP;

    DBMS_SQL.CLOSE_CURSOR(c_);

  END LOOP;

  dbms_output.put_line ('Script Execution Completed ..' );
END;
/

特别感谢 a_horse_with_no_name、Justin Cave、kevinsky、Alex Poole 和 Wernfried Domscheit 的支持:)

【讨论】:

以上是关于从 PLSQL 中所有动态找到的表中获取数据时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

PLSQL:将数据从一个表中插入可能的环境到另一个表,同时保持 from_table 名称动态

使用 Oracle PLSQL 从动态选择的表中合并唯一值的更有效方法

如何从加载在 sql/plsql 表中的 csv 文件中获取值

如何使用 pl sql 过程从结构仅在运行时知道的 oracle 表中动态获取数据?

在表中动态记录 PLSql 语句

plsql-Oracle 集合