在oracle存储过程

时间:2017-11-28 10:40:39

标签: oracle stored-procedures plsql

所以我有一个查询,我想通过存储过程执行并将查询的输出导出到CSV文件。所以我使用以下存储过程来执行此操作:

CREATE OR REPLACE PROCEDURE parseCSV(
p_file_dir         VARCHAR2, -- Oracle directory name
p_file_name     VARCHAR2, -- filename
p_sql_query        VARCHAR2, -- select * from table or some such query
p_delimiter     CHAR      -- column delimiter
)
AS

l_cursor_handle  INTEGER;
l_dummy              NUMBER;
l_col_cnt          INTEGER;
l_rec_tab            DBMS_SQL.DESC_TAB;
l_current_col      NUMBER(16);
l_current_line   VARCHAR2(2047);
l_column_value   VARCHAR2(300);
l_file_handle      UTL_FILE.FILE_TYPE;
l_print_text       VARCHAR2(100);
l_record_count   NUMBER(16) := 0;

BEGIN
   l_file_handle := UTL_FILE.FOPEN(p_file_dir, p_file_name, 'a', 2047); 
   l_cursor_handle := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(l_cursor_handle, p_sql_query, DBMS_SQL.native);
   l_dummy := DBMS_SQL.EXECUTE(l_cursor_handle);
   DBMS_SQL.DESCRIBE_COLUMNS(l_cursor_handle, l_col_cnt, l_rec_tab); 
   l_current_col := l_rec_tab.FIRST;
   IF (l_current_col IS NOT NULL) THEN
      LOOP
         DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);
         l_print_text := l_rec_tab(l_current_col).col_name || p_delimiter;
         UTL_FILE.PUT (l_file_handle, l_print_text);
         l_current_col := l_rec_tab.NEXT(l_current_col);
         EXIT WHEN (l_current_col IS NULL);
      END LOOP;
   END IF;
   UTL_FILE.PUT_LINE (l_file_handle,' ');
   LOOP
      EXIT WHEN DBMS_SQL.FETCH_ROWS(l_cursor_handle) = 0; 

      l_current_line := '';
      FOR l_current_col IN 1..l_col_cnt LOOP
         DBMS_SQL.COLUMN_VALUE (l_cursor_handle, l_current_col, l_column_value);
         l_print_text := l_column_value || p_delimiter;

         l_current_line := l_current_line || l_column_value || p_delimiter;
      END LOOP;
      l_record_count := l_record_count + 1;
      UTL_FILE.PUT_LINE (l_file_handle, l_current_line);
   END LOOP;
   UTL_FILE.FCLOSE (l_file_handle);
   DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
END;
/

执行时的过程处理查询,然后将结果存储到分隔文件中。例如,常规SELECT语句的过程输出将采用以下形式:

ID,ROLL_NO,RANK, 
1,123456,1620,
2,987654,1344,

现在这就是我的问题。正如您所看到的,输出文件中的每一行都以一个额外的尾随,结束。现在,由于我对plsql缺乏了解,我无法想到我可以对该过程进行的修改,以便预期的输出文件具有以下形式:

ID,ROLL_NO,RANK 
1,123456,1620
2,987654,1344

有人可以在这里帮助一个甲骨文新手并给我一些关于如何做到这一点的指示吗?我会非常感激。

6 个答案:

答案 0 :(得分:0)

尝试此程序:

CREATE OR REPLACE PROCEDURE parseCSV(
p_file_dir         VARCHAR2, -- Oracle directory name
p_file_name     VARCHAR2, -- filename
p_sql_query        VARCHAR2, -- select * from table or some such query
p_delimiter     CHAR      -- column delimiter
)
AS

l_cursor_handle  INTEGER;
l_dummy              NUMBER;
l_col_cnt          INTEGER;
l_rec_tab            DBMS_SQL.DESC_TAB;
l_current_col      NUMBER(16);
l_current_line   VARCHAR2(2047);
l_column_value   VARCHAR2(300);
l_file_handle      UTL_FILE.FILE_TYPE;
l_print_text       VARCHAR2(100);
l_record_count   NUMBER(16) := 0;

BEGIN
   l_file_handle := UTL_FILE.FOPEN(p_file_dir, p_file_name, 'a', 2047);
   l_cursor_handle := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(l_cursor_handle, p_sql_query, DBMS_SQL.native);
   l_dummy := DBMS_SQL.EXECUTE(l_cursor_handle);
   DBMS_SQL.DESCRIBE_COLUMNS(l_cursor_handle, l_col_cnt, l_rec_tab);
   l_current_col := l_rec_tab.FIRST;
   IF (l_current_col IS NOT NULL) THEN
      LOOP
         DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);
         IF l_print_text IS NOT NULL THEN
           l_print_text := l_print_text || p_delimiter;
         END IF;
         l_print_text := l_rec_tab(l_current_col).col_name;
         UTL_FILE.PUT (l_file_handle, l_print_text);
         l_current_col := l_rec_tab.NEXT(l_current_col);
         EXIT WHEN (l_current_col IS NULL);
      END LOOP;
   END IF;
   UTL_FILE.PUT_LINE (l_file_handle,' ');
   l_print_text := NULL;
   LOOP
      EXIT WHEN DBMS_SQL.FETCH_ROWS(l_cursor_handle) = 0;

      l_current_line := '';
      FOR l_current_col IN 1..l_col_cnt LOOP
         DBMS_SQL.COLUMN_VALUE (l_cursor_handle, l_current_col, l_column_value);
         IF l_print_text IS NOT NULL THEN
           l_print_text := l_print_text || p_delimiter;
         END IF;

         l_print_text := l_column_value;

      END LOOP;
      l_record_count := l_record_count + 1;
      UTL_FILE.PUT_LINE (l_file_handle, l_print_text );
   END LOOP;
   UTL_FILE.FCLOSE (l_file_handle);
   DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
END;

答案 1 :(得分:0)

首先,我要说导出CSV文件的程序应该命名为createCSVmakeCSV或类似名称,但绝不能parseCSV

接下来,我不确定这个应用程序设计是否最佳。通常,当外部客户端考虑媒体,格式等时,数据库应该担心数据。

最后,为了消除尾随分隔符,你应该使用这样的东西:

...
p_delimiter     CHAR      -- column delimiter
)
AS

l_delimiter     varchar2(1 char);

...

BEGIN
   ....
   l_current_col := l_rec_tab.FIRST;
   l_delimiter := '';
   ....
         l_print_text := l_delimiter || l_rec_tab(l_current_col).col_name;
         l_delimiter := p_delimiter;
   ....
/

答案 2 :(得分:0)

请尝试以下添加评论的地方

   CREATE OR REPLACE PROCEDURE parseCSV(
p_file_dir         VARCHAR2, -- Oracle directory name
p_file_name     VARCHAR2, -- filename
p_sql_query        VARCHAR2, -- select * from table or some such query
p_delimiter     CHAR      -- column delimiter
)
AS

    l_cursor_handle  INTEGER;
    l_dummy              NUMBER;
    l_col_cnt          INTEGER;
    l_rec_tab            DBMS_SQL.DESC_TAB;
    l_current_col      NUMBER(16);
    l_current_line   VARCHAR2(2047);
    l_column_value   VARCHAR2(300);
    l_file_handle      UTL_FILE.FILE_TYPE;
    l_print_text       VARCHAR2(100);
    l_record_count   NUMBER(16) := 0;

    BEGIN
       l_file_handle := UTL_FILE.FOPEN(p_file_dir, p_file_name, 'a', 2047); 
       l_cursor_handle := DBMS_SQL.OPEN_CURSOR;
       DBMS_SQL.PARSE(l_cursor_handle, p_sql_query, DBMS_SQL.native);
       l_dummy := DBMS_SQL.EXECUTE(l_cursor_handle);
       DBMS_SQL.DESCRIBE_COLUMNS(l_cursor_handle, l_col_cnt, l_rec_tab); 
       l_current_col := l_rec_tab.FIRST;
       IF (l_current_col IS NOT NULL) THEN
          LOOP
             DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);

             l_print_text := l_rec_tab(l_current_col).col_name || 
             p_delimiter;
             l_current_col := l_rec_tab.NEXT(l_current_col);
             IF l_current_col IS NULL/*handling for last delimiter for 
            column */
             THEN
             l_print_text:=substr(l_print_text,-1);
             END IF;
             UTL_FILE.PUT (l_file_handle, l_print_text);
             EXIT WHEN (l_current_col IS NULL);
          END LOOP;
       END IF;
       UTL_FILE.PUT_LINE (l_file_handle,' ');
       LOOP
          EXIT WHEN DBMS_SQL.FETCH_ROWS(l_cursor_handle) = 0; 

          l_current_line := '';
          FOR l_current_col IN 1..l_col_cnt LOOP
             DBMS_SQL.COLUMN_VALUE (l_cursor_handle, l_current_col, l_column_value);
             l_print_text := l_column_value || p_delimiter;
             IF l_current_col =l_col_cnt
             then
             l_current_line := l_current_line || l_column_value;
             ELSE
             l_current_line := l_current_line || l_column_value || 
             p_delimiter;
             END IF;
          END LOOP;
          l_record_count := l_record_count + 1;
          UTL_FILE.PUT_LINE (l_file_handle, l_current_line);
       END LOOP;
       UTL_FILE.FCLOSE (l_file_handle);
       DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
    END;

答案 3 :(得分:0)

你可以做两件事。第一种是更新对UTL_FILE.put的调用,以便有条件地添加分隔符(下面的示例为标题记录,但同样可以应用于数据):

;

第二种方法是构建一个包含完整数据行的字符串,然后在调用UTL FILE之前操作该字符串(在这种情况下,我假设l_print_text对于一行数据足够长):

     IF l_current_col < l_rec_tab.LAST THEN
       l_print_text := l_rec_tab(l_current_col).col_name || p_delimiter;
     ELSE
       l_print_text := l_rec_tab(l_current_col).col_name ;
     END IF ;
     UTL_FILE.PUT (l_file_handle, l_print_text);

答案 4 :(得分:0)

我已经改变了你的代码。用户此程​​序:

CREATE OR REPLACE PROCEDURE PARSECSV
(
  P_FILE_DIR  VARCHAR2, -- Oracle directory name
  P_FILE_NAME VARCHAR2, -- filename
  P_SQL_QUERY VARCHAR2, -- select * from table or some such query
  P_DELIMITER CHAR -- column delimiter
) IS

  L_CURSOR_HANDLE INTEGER;
  L_DUMMY         NUMBER;
  L_COL_CNT       INTEGER;
  L_REC_TAB       DBMS_SQL.DESC_TAB;
  L_COLUMN_VALUE  VARCHAR2(300);
  L_FILE_HANDLE   UTL_FILE.FILE_TYPE;
  L_PRINT_TEXT    CLOB;

BEGIN
  L_FILE_HANDLE   := UTL_FILE.FOPEN(P_FILE_DIR,
                                    P_FILE_NAME,
                                    'a',
                                    2047);
  L_CURSOR_HANDLE := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(L_CURSOR_HANDLE,
                 P_SQL_QUERY,
                 DBMS_SQL.NATIVE);
  DBMS_SQL.DESCRIBE_COLUMNS(L_CURSOR_HANDLE,
                            L_COL_CNT,
                            L_REC_TAB);

  FOR L_CURRENT_COL IN 1 .. L_COL_CNT
  LOOP
    DBMS_SQL.DEFINE_COLUMN(L_CURSOR_HANDLE,
                           L_CURRENT_COL,
                           L_COLUMN_VALUE,
                           300);

    IF L_PRINT_TEXT IS NOT NULL THEN
      L_PRINT_TEXT := L_PRINT_TEXT || P_DELIMITER;
    END IF;
    L_PRINT_TEXT := L_PRINT_TEXT || L_REC_TAB(L_CURRENT_COL).COL_NAME;
  END LOOP;
  L_PRINT_TEXT := L_PRINT_TEXT || CHR(10) || CHR(13);
  UTL_FILE.PUT(L_FILE_HANDLE,
               L_PRINT_TEXT);

  L_PRINT_TEXT := NULL;
  L_DUMMY      := DBMS_SQL.EXECUTE(L_CURSOR_HANDLE);
  DBMS_OUTPUT.PUT_LINE(L_DUMMY);

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

    FOR L_CURRENT_COL IN 1 .. L_COL_CNT
    LOOP
      DBMS_SQL.COLUMN_VALUE(L_CURSOR_HANDLE,
                            L_CURRENT_COL,
                            L_COLUMN_VALUE);
      IF L_PRINT_TEXT IS NOT NULL THEN
        L_PRINT_TEXT := L_PRINT_TEXT || P_DELIMITER;
      END IF;

      L_PRINT_TEXT := L_PRINT_TEXT || L_COLUMN_VALUE;

    END LOOP;
    L_PRINT_TEXT := L_PRINT_TEXT || CHR(10) || CHR(13);
    UTL_FILE.PUT(L_FILE_HANDLE,
                 L_PRINT_TEXT);
  END LOOP;
  UTL_FILE.FCLOSE(L_FILE_HANDLE);
  DBMS_SQL.CLOSE_CURSOR(L_CURSOR_HANDLE);
END;

答案 5 :(得分:0)

首先,在数据库中创建一个目录,并提供对该目录的读写访问权限。

要创建目录:

CREATE OR REPLACE DIRECTORY alias AS 'pathname';

要授予阅读权限,请写:

GRANT read,write ON DIRECTORY alias TO {user | role | PUBLIC};

然后,使用下面的存储过程获取任何文件格式的SQL查询的输出。

CREATE OR REPLACE PROCEDURE CSV_EXPORT AS
  CURSOR c_data IS
    SELECT * from table_name;

  v_file  UTL_FILE.FILE_TYPE;
BEGIN
  v_file := UTL_FILE.FOPEN(location     => 'FILES1',
                           filename     => 'csv_exp.txt',
                           open_mode    => 'w',
                           max_linesize => 32767);
  FOR cur_rec IN c_data LOOP
    UTL_FILE.PUT_LINE(v_file,
                      cur_rec.column1    || ',' ||
                      cur_rec.column2 );
  END LOOP;
  UTL_FILE.FCLOSE(v_file);

EXCEPTION
  WHEN OTHERS THEN
    UTL_FILE.FCLOSE(v_file);
    RAISE;
END;

要运行存储过程:

EXEC CSV_EXPORT;

代码FILES1中的目录名称。