将海量 CSV 文件读入 Oracle 表

Posted

技术标签:

【中文标题】将海量 CSV 文件读入 Oracle 表【英文标题】:Reading A Massive CSV file into an Oracle table 【发布时间】:2019-11-03 00:12:45 【问题描述】:

我有一个包含超过 200,000 行的巨大 CSV 文件。每行都有一个评论 ID、酒店 ID、评论日期和评论本身。评论栏引起了问题。有些评论很长,超过 2000 字节,因此会导致错误:

ORA-20000:ORU-10027:缓冲区溢出,限制为 20000 字节。

我能做些什么来解决这个问题吗?这是我当前的代码:

DECLARE
    F UTL_FILE.FILE_TYPE;
    v_Line VARCHAR2(2000);
    v_ReviewID NUMBER;
    v_HotelID VARCHAR2(2000);
    v_ReviewDate VARCHAR(2000);
    v_ReviewName CLOB;
    v_Review CLOB;
BEGIN
    F := UTL_FILE.FOPEN ('REVIEW_READ', 'Review.csv', 'R', 30000);

    IF UTL_FILE.IS_OPEN(F) THEN
        LOOP
            BEGIN
                DBMS_OUTPUT.PUT_LINE('Start');
                UTL_FILE.GET_LINE(F, v_Line);            
                v_ReviewID    := REGEXP_SUBSTR(v_Line, '[^,]+', 1, 1);
                v_HotelID     := REGEXP_SUBSTR(v_Line, '[^,]+', 1, 2);
                v_ReviewDate  := REGEXP_SUBSTR(v_Line, '[^,]+', 1, 3); 
                v_ReviewName  := REGEXP_SUBSTR(v_Line, '[^,]+', 1, 4);
                v_Review      := REGEXP_SUBSTR(v_Line, '[^,]+', 1, 5);

                INSERT INTO Review
                VALUES(v_ReviewID, v_HotelID, v_ReviewDate, v_ReviewName, v_Review);

                DBMS_OUTPUT.PUT_LINE(v_Line);

                IF v_Line IS NULL THEN
                    EXIT;
                END IF;

                COMMIT;
            EXCEPTION
                WHEN NO_DATA_FOUND THEN
                    EXIT;
            END;
        END LOOP;
    END IF;
EXCEPTION  
    WHEN OTHERS THEN  
        dbms_output.put_line(SQLERRM);
        UTL_FILE.FCLOSE(F);
END;

【问题讨论】:

请提及您正在使用的数据库软件:Oracle? 是的,我正在使用 Oracal 将评论文本的数据类型更改为CLOB 我进行了上述编辑,但我仍然恢复了以下错误:ORA-20000:ORU-10027:缓冲区溢出,限制为 20000 字节。评论长短不一,有些真的很大。有没有办法读取其中的一部分然后解析它然后读取其余部分? @NathanC 我已将此错误消息添加到您的问题中。我认为这是您遇到的主要问题,因为您提到了“缓冲区溢出错误”,但如果 VARCHAR2 变量或表的列对于您要加载的内容来说不够大,也可能会出现问题。在声明列时使用tablename.columnname%type 语法也是一个好主意,这样您就知道一切都是同步的。 【参考方案1】:

您的代码存在 2 个问题。 @Steve Friedl 已经提到了一个。另一个如下:

如果您在PL/SQL 中使用大量dbms_output.put_line 且缓冲量很小,则会出现ORU-10027。 如果您只是在调试,那么您应该将其设置为很大(dbms_output.enable(10000000) 是传统的,尽管从 10g 开始它可以不受限制。

所以如果你在你的代码中评论这一行 DBMS_OUTPUT.PUT_LINE(V_LINE); 并将此 DBMS_OUTPUT.PUT_LINE('Start'); 置于循环之外,它应该可以正常工作。

【讨论】:

【参考方案2】:

我不知道这个数据库系统,但它看起来像这样一行:

v_Review  := REGEXP_SUBSTR(V_LINE, '[^,]+', 1, 5);

通过尝试将过长的字符串分配给 2000 限制 v_Review 给您带来了问题。也许使用SUBSTR 只收集该字符串的前 2000 个字符可能有效?

v_Review  := SUBSTR( REGEXP_SUBSTR(V_LINE, '[^,]+', 1, 5), 0, 2000 ); // maybe?

【讨论】:

这不起作用。我仍然得到同样的错误:ORA-20000: ORU-10027: buffer overflow, limit of 20000 bytes【参考方案3】:

尝试删除所有dbms_output 行并重新检查结果。我的猜测是你只是把一个很长的输出放到缓冲区中。 此外,如果您必须将一次性 CSV 文件加载到 Oracle 数据库中,请查看 sqlldr 实用程序(例如,https://www.orafaq.com/wiki/SQL*Loader_FAQ)。它为您完成所有工作,您只需创建一个目标表并进行一些设置。

【讨论】:

以上是关于将海量 CSV 文件读入 Oracle 表的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从单个 csv/excel 文件传输到多个 oracle 表

如何将带有 NULL 值的引用 CSV 读入 Amazon Athena

将多个 CSV 文件读入单独的数据帧

Python将整数从csv文件读入列表

将文件夹中的多个csv文件读入R中的单个数据框[重复]

将csv文件作为python中的变量读入