miércoles, 9 de octubre de 2013

Función para crear archivo CSV a partir de consulta Oracle

Recientemente tuve la necesidad de crear un Stored Procedure para crear archivos de texto delimitados, como resultados de varias consultas a la Base de Datos. Les dejo la rutina que invoca y la función que realiza el proceso:

/***************************************************/
/*************** BLOQUE ORIGINANTE *****************/
/***************************************************/
DECLARE
   RetVal   NUMBER;
BEGIN
   RetVal :=
      schDBA.dump_csv ('SELECT * FROM SIEBEL.S_OPTY',
                      'SIEBEL_EIM_DIR',
                      'S_OPTY.CSV',
                      '|',
                      true,
                      false,
                      32000,
                      'W'
                     );
END;

/*****************************************************/
/********* PROCESAMIENTO DE LA CONSULTA **************/
/*****************************************************/
CREATE OR REPLACE FUNCTION dump_csv (
   p_query                IN   VARCHAR2,
   p_dir                  IN   VARCHAR2,
   p_filename             IN   VARCHAR2,
   p_separator            IN   VARCHAR2,
   p_headers              IN   BOOLEAN DEFAULT FALSE,
   p_trailing_separator   IN   BOOLEAN DEFAULT FALSE,
   p_max_linesize         IN   NUMBER DEFAULT 32000,
   p_mode                 IN   VARCHAR2 DEFAULT 'w'
)
   RETURN NUMBER
IS
   l_output        UTL_FILE.FILE_TYPE;
   l_thecursor     INTEGER            DEFAULT DBMS_SQL.open_cursor;
   l_columnvalue   VARCHAR2 (4000);
   l_status        INTEGER;
   l_colcnt        NUMBER             DEFAULT 0;
   l_cnt           NUMBER             DEFAULT 0;
   l_separator     VARCHAR2 (10)      DEFAULT '';
   l_line          LONG;
   l_desctbl       DBMS_SQL.desc_tab;
   v_sqlerrm       VARCHAR2 (32000);
   l_mode          CHAR (1)           := 'w';
BEGIN
   IF p_mode NOT IN ('w', 'a')
   THEN
      l_mode := 'w';
   ELSE
      l_mode := p_mode;
   END IF;

   l_output := UTL_FILE.fopen (p_dir, p_filename, l_mode, p_max_linesize);
   DBMS_SQL.parse (l_thecursor, p_query, DBMS_SQL.native);
   DBMS_SQL.describe_columns (l_thecursor, l_colcnt, l_desctbl);

   FOR i IN 1 .. l_colcnt
   LOOP
      DBMS_SQL.define_column (l_thecursor, i, l_columnvalue, 4000);

      IF (l_desctbl (i).col_type = 2)                       /* number type */
      THEN
         l_desctbl (i).col_max_len := l_desctbl (i).col_precision + 2;
      ELSIF (l_desctbl (i).col_type = 12)                      /* date type */
      THEN
/* length of my date format */
         l_desctbl (i).col_max_len := 20;
      ELSIF (l_desctbl (i).col_type = 8)                       /* LONG type */
      THEN
         l_desctbl (i).col_max_len := 2000;
      END IF;

      IF p_headers
      THEN
         UTL_FILE.put (l_output, l_separator || l_desctbl (i).col_name);
         l_separator := p_separator;
      END IF;
   END LOOP;

   IF p_trailing_separator
   THEN
      UTL_FILE.put (l_output, l_separator);
   END IF;

   IF p_headers
   THEN
      UTL_FILE.new_line (l_output);
   END IF;

   l_status := DBMS_SQL.EXECUTE (l_thecursor);

   LOOP
      EXIT WHEN (DBMS_SQL.fetch_rows (l_thecursor) <= 0);
      l_line := NULL;
      l_separator := '';

      FOR i IN 1 .. l_colcnt
      LOOP
         DBMS_SQL.COLUMN_VALUE (l_thecursor, i, l_columnvalue);

         IF NVL (INSTR (l_columnvalue, ','), 0) = 0
         THEN
            NULL;
         ELSE
            l_columnvalue := '"' || l_columnvalue || '"';
         END IF;

         UTL_FILE.put (l_output, l_separator || l_columnvalue);
         l_separator := p_separator;
      END LOOP;

      IF p_trailing_separator
      THEN
         UTL_FILE.put (l_output, l_separator);
      END IF;

      UTL_FILE.new_line (l_output);
      l_cnt := l_cnt + 1;
   END LOOP;

   DBMS_SQL.close_cursor (l_thecursor);
   UTL_FILE.fclose (l_output);
   RETURN l_cnt;
EXCEPTION
   WHEN NO_DATA_FOUND
   THEN
      DBMS_OUTPUT.put_line ('NO_DATA_FOUND');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.invalid_path
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INVALID_PATH');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.read_error
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.READ_ERROR');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.write_error
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.WRITE_ERROR');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.invalid_mode
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INVALID_MODE');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.invalid_filehandle
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INVALID_FILEHANDLE');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.invalid_operation
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INVALID_OPERATION');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.internal_error
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INTERNAL_ERROR');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN UTL_FILE.invalid_maxlinesize
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.INVALID_MAXLINESIZE');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN VALUE_ERROR
   THEN
      DBMS_OUTPUT.put_line ('UTL_FILE.VALUE_ERROR');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
   WHEN OTHERS
   THEN
      --hum_do.default_exception ('ERROR in dump_csv : ');
      DBMS_OUTPUT.put_line ('ERROR in dump_csv : ');
      UTL_FILE.fclose (l_output);
      RETURN l_cnt;
END dump_csv;