,如果你的源列实际上是一个NCLOB,而不是CLOB你会得到这个错误。这是OK:
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;
但是这样的错误,只是改变CLOB到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
这是AL32UTF8和AL16UTF16作为数据库和国家字符集。
所以如果你的源表是NCLOB,你只能提取前2000个字符放到你的stream_text
表中。
如果源列是CLOB并且前4000个字符包含任何多字节字符,那么您还会看到这一点。 dbms_log.substr(x, 4000, 1)
总是获取CLOB的前4000个字符,该字符可能超过4000个字节 - 这是SQL上下文中VARCHAR2值的最大大小,即使它声明为varchar2(4000 char)
,因为它仍然不能超过4000-字节限制。
如果你想获得最大的4000个字符了,那么你可以做到这一点通过PL/SQL VARCHAR2变量,另外还有substrb()
电话:
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;
/
的substrb(..., 1, 4000)
部分将无法在SQL工作,要么,因为内部表达仍然过大,但它在PL/SQL中起作用。您将获得前4000个字符的前4000个字节。 (尽管如果第4000个字节是通过多字节字符的中途,您仍然可能会遇到问题)。
我猜对了目标表中的列名,所以很明显使用真正的列名。如果你有大量的数据,做批量插入会更好;获取到一个集合中,并使用FORALL来批量插入而不是逐行插入;像这样的东西可以作为一个起点:
DECLARE
TYPE TMP_REC_TYPE IS RECORD (
ID STREAM_TEXT.ID%TYPE,
POST_TEXT ACTIVITY_EVENT.POST_TEXT%TYPE,
TEXT STREAM_TEXT.TEXT%TYPE,
DT STREAM_TEXT.DT%TYPE,
STATE STREAM_TEXT.STATE%TYPE,
STREAM_ID STREAM_TEXT.STREAM_ID%TYPE
);
TYPE TMP_REC_TAB_TYPE IS TABLE OF TMP_REC_TYPE;
TMP_REC_TAB TMP_REC_TAB_TYPE;
RC SYS_REFCURSOR;
BEGIN
OPEN RC FOR
SELECT SEQ$STREAM_TEXT.NEXTVAL AS ID,
T.POST_TEXT,
NULL AS 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;
LOOP
FETCH RC BULK COLLECT INTO TMP_REC_TAB LIMIT 100;
FOR I IN 1..TMP_REC_TAB.COUNT LOOP -- populate text field
TMP_REC_TAB(I).TEXT := SUBSTRB(
DBMS_LOB.SUBSTR(TMP_REC_TAB(I).POST_TEXT, 4000, 1), 1, 4000);
END LOOP;
FORALL I IN 1..TMP_REC_TAB.COUNT -- bulk insert
INSERT INTO STREAM_TEXT (ID, TEXT, DT, STATE, STREAM_ID)
VALUES (TMP_REC_TAB(I).ID, TMP_REC_TAB(I).TEXT, TMP_REC_TAB(I).DT,
TMP_REC_TAB(I).STATE, TMP_REC_TAB(I).STREAM_ID);
EXIT WHEN RC%NOTFOUND;
END LOOP;
END;
/
请为涉及的表添加DDL语句。另外,你有没有尝试在'INSERT'中添加列列表?在“INSERT”语句中省略列表是不好的做法 - 这将保证将来会导致维护问题。 –
这会失败,同样的问题'SELECT t.id,DBMS_LOB.substr(t.post_text,4000,1)FROM ACTIVITY_EVENT t WHERE t.POST_TEXT IS NOT NULL' –
我对请求做了一些改动。我认为缓冲区大小的问题。但不知道如何清除缓冲区。 –