使用dbms_lob.substr的迁移脚本会使#34;字符串缓冲区太小而且#34;

时间:2015-05-28 13:29:23

标签: oracle plsql clob

我遇到了将数据从旧表迁移到新表的脚本问题。旧表中的一列是CLOB,但在新表中它是VARCHAR2。我尝试使用下面的代码插入那些。但是我遇到了错误:

  

ORA-06502:PL / SQL:数字或值错误:字符串缓冲区太小。

DECLARE
    CURSOR CUR IS
        SELECT T.*
          FROM ACTIVITY_EVENT T
         WHERE T.POST_TEXT IS NOT NULL;
    R CUR%ROWTYPE;
BEGIN
 FOR R IN CUR
    LOOP
        INSERT INTO STREAM_TEXT
            WITH STR AS
             (SELECT T.*
                FROM STREAM T
               WHERE T.OLD_ID = R.ID)
            SELECT SEQ$STREAM_TEXT.NEXTVAL AS ID,
                   DBMS_LOB.SUBSTR(T.POST_TEXT, 4000, 1) AS TEXT,
                   T.DT AS DT,
                   'READY' AS STATE,
                   STR.ID AS STREAM_ID
              FROM ACTIVITY_EVENT T
              LEFT JOIN STR
                ON STR.OLD_ID = T.ID
             WHERE T.POST_TEXT IS NOT NULL
               AND STR.OLD_ID = T.ID;
    END LOOP;
END;

我在没有循环的情况下完成了这个代码并得到了同样的问题所以我试图创建一个循环。但结果是一样的。

此简单查询失败并出现相同的错误:

SELECT T.ID, DBMS_LOB.SUBSTR(T.POST_TEXT, 4000, 1)
FROM ACTIVITY_EVENT T
WHERE T.POST_TEXT IS NOT NULL

1 个答案:

答案 0 :(得分:3)

如果您的源列实际上是NCLOB而不是CLOB,那么您将收到此错误。这没关系:

{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0x3edba223fdd11309a7f277882e0bfaa287f2676b","to":"0x42ec4438503f690dcc2939047e1e8cf6b9218f95","value":1}],"id":1}

但是这个错误,只是将CLOB更改为NCLOB,任何长度都超过2000:

create table t42 (id number, dt date, post_text clob);
insert into t42 (id, dt, post_text) values (1, sysdate, dbms_random.string('p', 4000));
select id, dbms_lob.substr(post_text, 4000, 1) from t42;

这是AL32UTF8和AL16UTF16作为数据库和国家字符集。

因此,如果您的源表是NCLOB,则只能提取前2000个字符以放入create table t42 (id number, dt date, post_text nclob); insert into t42 (id, dt, post_text) values (1, sysdate, dbms_random.string('p', 4000)); select id, dbms_lob.substr(post_text, 4000, 1) from t42; SQL Error: ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512: at line 1 表。

如果源列是CLOB并且前4000个字符包含任何多字节字符,您也会看到这一点。 stream_text总是得到CLOB的前4000个字符,可能超过4000个字节 - 这是SQL上下文中VARCHAR2值的最大大小,即使它被声明为{{1}因为它仍然不能超过4000字节的限制。

如果您想获得最多4000个字符,那么您可以通过PL / SQL VARCHAR2变量执行此操作,并进行额外的substrb()调用:

dbms_log.substr(x, 4000, 1)

varchar2(4000 char)部分在SQL中也不起作用,因为内部表达式仍然太大,但它在PL / SQL中有效。您获得前4000个字符的前4000个字节。 (尽管如果第4000个字节是通过多字节字符的中途,你可能仍有问题。)

我已经猜到了目标表中的列名,所以显然使用真实的。如果您有大量数据,那么批量插入会更好;获取集合并使用FORALL批量插入而不是逐行插入;这样的事情可以作为一个起点:

DECLARE
    CURSOR CUR IS
        SELECT SEQ$STREAM_TEXT.NEXTVAL AS ID,
               T.POST_TEXT,
               T.DT AS DT,
               'READY' AS STATE,
               S.ID AS STREAM_ID
          FROM ACTIVITY_EVENT T
          LEFT JOIN STREAM S
            ON S.OLD_ID = T.ID
         WHERE T.POST_TEXT IS NOT NULL;

    TMP_TEXT VARCHAR2(4000);
BEGIN
    FOR R IN CUR
    LOOP
        TMP_TEXT := SUBSTRB(DBMS_LOB.SUBSTR(R.POST_TEXT, 4000, 1), 1, 4000);
        INSERT INTO STREAM_TEXT (ID, TEXT, DT, STATE, STREAM_ID)
        VALUES (R.ID, TMP_TEXT, R.DT, R.STATE, R.STREAM_ID);
    END LOOP;
END;
/