如何使用UTF_FILE Oracle

时间:2018-08-13 03:30:42

标签: sql database oracle plsql oracle11g

在Oracle中使用UTF_FILE是我的新手。我有一个 CSV 文件,包含30万条记录和6列,这些列用逗号“,” 分隔。

我希望将所有这些数据拆分并设置为某些变量,并通过Pl / SQL插入到某些表或列中,但是我该怎么做呢?

目前,执行pl / sql的时间并不重要 [Oracle 11gR2 XE]

2 个答案:

答案 0 :(得分:1)

您要解决的问题:

DECLARE
    v_row VARCHAR2(200) := 'a,b,c';
BEGIN
    FOR v_col IN (SELECT REGEXP_SUBSTR (v_row, '[^,]+', 1, LEVEL) cell FROM DUAL CONNECT BY REGEXP_SUBSTR (v_row, '[^,]+', 1, LEVEL) IS NOT NULL)
    LOOP
        DBMS_OUTPUT.PUT_LINE (v_col.cell);
    END LOOP;
END;

但是,正如评论所说:您不应该这样做。使用SQL Loader:Oracle: Import CSV file

答案 1 :(得分:1)

嗨,我几年前已经使用过此代码,并且可以正常工作

您可以将其更改为使用regexp而不是复杂的 substr s

您可以使用索引表而不是我使用的嵌套表类型,可以将嵌套插入表中,但是 index-by 仅在pl / sql上可用,不能作为参数传递给其他存储的程序(可以嵌套)。

CREATE OR REPLACE TYPE ARRAY_OF_TEXTS AS TABLE OF VARCHAR2 (4000);

CREATE OR REPLACE TYPE ARRAY_OF_ARRAYS AS TABLE OF ARRAY_OF_TEXTS;

CREATE OR REPLACE FUNCTION SPLIT (
   TEXT                  IN CLOB,
   C_SEPARATOR           IN VARCHAR2,
   IGNORE_EMBTY_BLOCKS   IN BOOLEAN DEFAULT FALSE)
   RETURN ARRAY_OF_TEXTS
IS
   S_LINE     VARCHAR2 (4000);
   S_TABLE    ARRAY_OF_TEXTS;
   I          INTEGER;
   OFFSET1    INTEGER := 1;
   OFFSET2    INTEGER;
   TEXT_LEN   INTEGER;
BEGIN
   S_TABLE := ARRAY_OF_TEXTS ();
   OFFSET2 := INSTR (TEXT, C_SEPARATOR, OFFSET1);
   TEXT_LEN := DBMS_LOB.GETLENGTH (TEXT);

   IF OFFSET2 < 1 --if there is no c_separator (if offset2 is 0) or there is not any c_separator at the end of text
   THEN
      OFFSET2 := TEXT_LEN;
   END IF;

   WHILE     (OFFSET2 = OFFSET1 + LENGTH (C_SEPARATOR) OR OFFSET2 = OFFSET1)
         AND DBMS_LOB.SUBSTR (TEXT, LENGTH (C_SEPARATOR), OFFSET1) =
                C_SEPARATOR         -- if there are 2 c_separator sequentially
   LOOP
      IF NOT IGNORE_EMBTY_BLOCKS
      THEN
         S_TABLE.EXTEND;
         S_TABLE (S_TABLE.LAST) := NULL;
      END IF;

      OFFSET1 := OFFSET2 + LENGTH (C_SEPARATOR);
      OFFSET2 := DBMS_LOB.INSTR (TEXT, C_SEPARATOR, OFFSET1);
   END LOOP;

   IF OFFSET2 > OFFSET1 + LENGTH (C_SEPARATOR)
   THEN
      S_TABLE.EXTEND;

      S_LINE := DBMS_LOB.SUBSTR (TEXT, OFFSET2 - OFFSET1, OFFSET1);
      S_TABLE (S_TABLE.LAST) := S_LINE;
      OFFSET1 := OFFSET2 + LENGTH (C_SEPARATOR);
   END IF;

   OFFSET2 := DBMS_LOB.INSTR (TEXT, C_SEPARATOR, OFFSET1);

   WHILE OFFSET2 > 1
   LOOP
      S_TABLE.EXTEND;

      S_LINE := DBMS_LOB.SUBSTR (TEXT, OFFSET2 - OFFSET1, OFFSET1);
      S_TABLE (S_TABLE.LAST) := S_LINE;
      OFFSET1 := OFFSET2 + LENGTH (C_SEPARATOR);
      OFFSET2 := DBMS_LOB.INSTR (TEXT, C_SEPARATOR, OFFSET1);

      WHILE     (   OFFSET2 = OFFSET1 + LENGTH (C_SEPARATOR)
                 OR OFFSET2 = OFFSET1)
            AND DBMS_LOB.SUBSTR (TEXT, LENGTH (C_SEPARATOR), OFFSET1) =
                   C_SEPARATOR      -- if there are 2 c_separator sequentially
      LOOP
         IF NOT IGNORE_EMBTY_BLOCKS
         THEN
            S_TABLE.EXTEND;
            S_TABLE (S_TABLE.LAST) := NULL;
         END IF;

         OFFSET1 := OFFSET2 + LENGTH (C_SEPARATOR);
         OFFSET2 := DBMS_LOB.INSTR (TEXT, C_SEPARATOR, OFFSET1);
      END LOOP;
   END LOOP;

   IF OFFSET1 < TEXT_LEN
   THEN
      S_TABLE.EXTEND;
      S_LINE :=
         DBMS_LOB.SUBSTR (
                          TEXT,
                          TEXT_LEN - OFFSET1 + LENGTH (C_SEPARATOR),
                          OFFSET1
                         );
      S_TABLE (S_TABLE.LAST) := S_LINE;
   ELSIF     OFFSET1 = TEXT_LEN
         AND DBMS_LOB.SUBSTR (TEXT, LENGTH (C_SEPARATOR), OFFSET1) <>
                C_SEPARATOR
   THEN
      S_TABLE.EXTEND;
      S_LINE.TEXT := DBMS_LOB.SUBSTR (TEXT, LENGTH (C_SEPARATOR), OFFSET1);
      S_TABLE (S_TABLE.LAST) := S_LINE;
   END IF;

   RETURN S_TABLE;
END;

CREATE OR REPLACE FUNCTION LOAD_CSV_FILE (
                                          P_DIRECTORY   IN VARCHAR2,
                                          P_FILENAME    IN VARCHAR2
                                         )
   RETURN ARRAY_OF_ARRAYS
AS
   SEPARATOR1   VARCHAR2 (2) := CHR (10);
   -- In Some Cases you should use: CHR (13) || CHR (10);
   SEPARATOR2   VARCHAR2 (1) := ',';
   --if csv separator is ; or | use it here
   V_TEXT       CLOB;
   V_BFILE      BFILE;

   V_LINES      ARRAY_OF_TEXTS;
   V_LINE       ARRAY_OF_TEXTS;
   V_ARRAY      ARRAY_OF_ARRAYS;
BEGIN
   SELECT EMPTY_CLOB () INTO V_TEXT FROM DUAL;
   --V_TEXT := EMPTY_CLOB ();
    DBMS_LOB.CREATETEMPORARY(V_TEXT, TRUE);

   V_BFILE := BFILENAME (P_DIRECTORY, P_FILENAME);

   IF DBMS_LOB.FILEEXISTS (V_BFILE) <> 1
   THEN
      RETURN NULL;
   END IF;

   DBMS_LOB.FILEOPEN (V_BFILE, DBMS_LOB.FILE_READONLY);
   DBMS_LOB.LOADFROMFILE (V_TEXT, V_BFILE, DBMS_LOB.GETLENGTH (V_BFILE));
   DBMS_LOB.FILECLOSE (V_BFILE);

   V_LINES := SPLIT2 (V_TEXT, SEPARATOR1);
   V_ARRAY := ARRAY_OF_ARRAYS ();

   FOR R_LINE IN (SELECT *
                  FROM TABLE (V_LINES))
   LOOP
      V_LINE := SPLIT(R_LINE.COLUMN_VALUE, SEPARATOR2);
      V_ARRAY.EXTEND ();
      V_ARRAY (V_ARRAY.LAST) := V_LINE;
   END LOOP;

   RETURN V_ARRAY;
END;

现在您可以像这样使用它:

DECLARE
   V_ARR    ARRAY_OF_ARRAYS;
   V_LINE   VARCHAR2 (4000);
BEGIN
   V_ARR := LOAD_CSV_FILE ('MY_DIR', 'file.csv');

   FOR LINE IN (SELECT *
                FROM TABLE (V_ARR))
   LOOP
      FOR FIELD IN (SELECT *
                    FROM TABLE (LINE.COLUMN_VALUE))
      LOOP
         DBMS_OUTPUT.PUT_LINE ('field:' || FIELD.COLUMN_VALUE);
      END LOOP;

      DBMS_OUTPUT.PUT_LINE ('end of line');
   END LOOP;
END;