【问题标题】:PL/SQL file writing with generic input使用通用输入写入 PL/SQL 文件
【发布时间】:2016-01-19 13:07:59
【问题描述】:

我最近创建了一个 PL/SQL 程序,它根据数据库中的相关数据创建五个不同的管道分隔文件。

在这种情况下,我找不到将不同的表格数据动态拉入到创建文件的通用过程中的方法。 相反,我必须创建五个单独的过程,每个文件一个,接收五个不同的游标,每个文件需求记录选择一个。

我不禁认为必须有更好的方法。我正在研究参考游标,但我不认为它们正是我正在寻找的。​​p>

如何在 PL/SQL 中实现这一点?

我认为我正在寻找的是一些通用类型,它可以在给定任意数量的记录和记录列的情况下从游标中获取任何数据,并且能够查询自身以查找其中的数据。

【问题讨论】:

  • 您希望导出数据(例如,导出为 .csv)还是使用 UTL_FILE 写入?
  • 程序在oracle电子商务套件中作为并发请求运行。所以我想我需要使用 UTL_FILE 来编写它。但是,要求是在这个“程序”运行之后,结果是目录中的五个新文件出站。因此,只要它不使用 UTL_FILE 导出数据并且结果是五个 csv 文件,那么您就可以开始了。感谢您的澄清
  • 这是一个关于主题的good article,它还提供了一个通用的导出到 csv 过程。
  • 感谢 Neria,我阅读了这篇文章。它给了我很多想法,这似乎适用于一张桌子。我创建的每个文件都是从复杂的游标创建的,这些游标接受参数并连接许多表,其中一个甚至连接 12 个表。这些参数在调用包时被发送到游标,因此游标是动态的,直到列保持一致(你发给我的这篇文章确实解决了这个问题;))但数据在变化时保持不变金额。我将更详细地更新我的答案,因为我现在看到我所做的一些假设引入了限制
  • 要在包含 sql select 的文章中添加(注释字符限制)varchar 变量,必须接受我的光标文本,然后为每个表添加一个参数,每个表的参数都不同。所以我仍然需要五个单独的过程来接收不同数量的表和表名。另外,我不知道如何介绍这些表连接的字段。这确实解决了我在固定列数量时遇到的问题,并且我看到他们如何使用 dbms_describe_columns 来放弃这个,所以我感谢你让我注意到这一新知识。

标签: oracle oop plsql utl-file


【解决方案1】:

将光标作为SYS_REFCURSOR 传递到您的过程中。然后,使用DBMS_SQL.TO_CURSOR_NUMBER(); 将引用游标转换为 DBMS_SQL 游标。

然后,使用DBMS_SQL.DESCRIBE_COLUMNS 找出游标中的列,并使用DBMS_SQL.DEFINE_COLUMNDBMS_SQL.FETCH_ROWSDBMS_SQL.VALUE 将游标中的数据获取到PL/SQL 变量中。然后,将 PL/SQL 变量写入输出文件。

这里有一些代码可以为您整合所有这些。

DECLARE
  l_rc SYS_REFCURSOR; 

PROCEDURE dump_cursor (p_rc IN OUT SYS_REFCURSOR) IS
  -- Dump the results of p_rc to log

  l_cursor                INTEGER;
  l_column_count          INTEGER;
  l_column_descriptions   SYS.DBMS_SQL.desc_tab;
  l_status                INTEGER;
  l_column_value          VARCHAR2 (4000);
  l_column_width          NUMBER;
  l_rec_count             NUMBER := 0;
  l_line                  VARCHAR2 (4000);


  FUNCTION get_length (l_column_def IN SYS.DBMS_SQL.desc_rec)
    RETURN NUMBER IS
    l_width   NUMBER;
  BEGIN
    l_width   := l_column_def.col_max_len;
    l_width   := CASE l_column_def.col_type WHEN 12 THEN                                                      /* DATE */
                                                        20 WHEN 2 THEN                                      /* NUMBER */
                                                                      10 ELSE l_width END;
    -- Don't display more than 256 characters of any one column (this was my requirement -- your file writer probably doesn't need to do this
    l_width   := LEAST (256, GREATEST (l_width, l_column_def.col_name_len));
    RETURN l_width;
  END get_length;
BEGIN
  -- This is the date format that I want to use for dates in my output
  EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY HH24:MI:SS''';

  l_cursor   := sys.DBMS_SQL.to_cursor_number (p_rc);

  -- Describe columns
  sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);

  l_line     := '';

  FOR i IN 1 .. l_column_count LOOP
    l_column_width   := get_length (l_column_descriptions (i));

    l_line           := l_line || RPAD (l_column_descriptions (i).col_name, l_column_width);
    l_line           := l_line || ' ';
    DBMS_SQL.define_column (l_cursor,
                            i,
                            l_column_value,
                            4000);
  END LOOP;

  DBMS_OUTPUT.put_line (l_line);

  l_line     := '';

  FOR i IN 1 .. l_column_count LOOP
    l_column_width   := get_length (l_column_descriptions (i));

    l_line           := l_line || RPAD ('-', l_column_width, '-');
    l_line           := l_line || ' ';
    DBMS_SQL.define_column (l_cursor,
                            i,
                            l_column_value,
                            4000);
  END LOOP;

  DBMS_OUTPUT.put_line (l_line);

  --   l_status   := sys.DBMS_SQL.execute (l_cursor);

  WHILE (sys.DBMS_SQL.fetch_rows (l_cursor) > 0) LOOP
    l_rec_count   := l_rec_count + 1;

    l_line        := '';

    FOR i IN 1 .. l_column_count LOOP
      DBMS_SQL.COLUMN_VALUE (l_cursor, i, l_column_value);
      l_column_value   := TRANSLATE (l_column_value, CHR (10), CHR (200));
      l_column_width   := get_length (l_column_descriptions (i));

      IF l_column_value IS NULL THEN
        l_line   := l_line || RPAD (' ', l_column_width);
      ELSE
        l_line   := l_line || RPAD (l_column_value, l_column_width);
      END IF;

      l_line           := l_line || ' ';
    END LOOP;

    DBMS_OUTPUT.put_line (l_line);
  END LOOP;

  IF l_rec_count = 0 THEN
    DBMS_OUTPUT.put_line ('No data found.');
  ELSE
    DBMS_OUTPUT.put_line (l_rec_count || ' rows returned.');
  END IF;

  sys.DBMS_SQL.close_cursor (l_cursor);

  -- It would be better to store the current NLS_DATE_FORMAT on entry and restore it here, instead of assuming that it was
  -- set to DD-MON-YYYY.
  EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
EXCEPTION
  WHEN OTHERS THEN
    EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
-- Add your own handling here.

END dump_cursor;

-- Tester code, make sure server output is on
BEGIN
  OPEN l_rc FOR 'SELECT object_id, object_name, object_type FROM dba_objects WHERE rownum <= 15';
  dump_cursor(l_rc);
END;

【讨论】:

  • 感谢您的回答。我学到了更多关于 Ref 游标的知识,许多文档无法简单地拼写出来。这绝对是我想要的。
猜你喜欢
  • 2011-09-01
  • 2018-07-26
  • 1970-01-01
  • 2016-07-09
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-04
  • 2014-08-17
相关资源
最近更新 更多