【问题标题】:Export query result to csv in oracle stored procedure在 oracle 存储过程中将查询结果导出到 csv
【发布时间】:2017-11-28 10:40:38
【问题描述】:

所以我有一个查询,我想通过存储过程执行并将查询的输出导出到 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

有人可以在这里帮助一个 Oracle 新手并给我一些关于如何做到这一点的指示吗?我将不胜感激。

【问题讨论】:

    标签: oracle stored-procedures plsql


    【解决方案1】:

    请尝试以下已添加 cmets 的地方

       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;
    

    【讨论】:

    • 您好,您能展示一下整个过程的样子吗?请多多包涵,我只是甲骨文新手
    • 嗨,我已经添加了相同的
    • 在输出文件头中很好,但没有任何值出现。看起来像这样:ID,MSISDN,RECHARGE_DATE,RECHARGE_AMOUNT -- in next line , -- next line , -- next line , -- next line
    • 你能看一下吗?
    • 检查表是否有数据
    【解决方案2】:

    首先,我想说导出 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;
       ....
    /
    

    【讨论】:

      【解决方案3】:

      试试这个过程:

      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;
      

      【讨论】:

      • 感谢您的快速回复,但输出文件的生成方式不正确。文件看起来像这样:IDROLL_NORANK 1 123456 1620 2 987654 1344。你能检查一下吗?
      【解决方案4】:

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

           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);
      

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

         IF (l_current_col IS NOT NULL) THEN
            LOOP
               DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);
               --append to variable
               l_print_text := l_print_text||l_rec_tab(l_current_col).col_name || p_delimiter;
               l_current_col := l_rec_tab.NEXT(l_current_col);
               EXIT WHEN (l_current_col IS NULL);
            END LOOP;
         END IF;
         --trim trailing delimiter
         l_print_text := TRIM(TRAILING p_delimiter FROM l_print_text) ;
         --send whole line to file
         UTL_FILE.PUT_LINE (l_file_handle,l_print_text);
      

      【讨论】:

        【解决方案5】:

        我稍微修改了你的代码。使用此程序:

        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;
        

        【讨论】:

          【解决方案6】:

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

          创建目录:

          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 是目录名。

          【讨论】:

            猜你喜欢
            • 2022-01-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-01-02
            • 1970-01-01
            • 1970-01-01
            • 2018-09-04
            相关资源
            最近更新 更多