LONG 是 Oracle 早期用於儲存超大文本(上限 2 GB)的資料型別,雖然 12c 起官方建議改用 CLOB,但大量舊系統仍在使用。本文系統梳理其特性、嚴格限制,並提供 PL/SQL 跨表複製與 Java setCharacterStream 插入超長文本的實戰方案。
VARCHAR2 或 CLOB + Oracle Text。SELECT 查詢UPDATE ... SET long_col = ...INSERT ... VALUES (..., long_value)| # | 限制項目 | 說明 |
|---|---|---|
| 1 | 單表僅一欄 | 一張表只能定義一個 LONG 欄位 |
| 2 | 不可建索引 | 無法創建任何類型索引 |
| 3 | 不可叢集 | 含 LONG 欄位的表不能建立叢集 |
| 4 | 跨表 INSERT SELECT 失效 | INSERT INTO t2 SELECT * FROM t1 會報 ORA-00997 |
| 5 | CTAS 失效 | CREATE TABLE t2 AS SELECT * FROM t1 同報錯 |
| 6 | 約束極少 | 僅支援 NULL/NOT NULL/DEFAULT,不能做 PK |
| 7 | 查詢子句受限 | 不可用於 WHERE/GROUP BY/ORDER BY/DISTINCT |
| 8 | 分布式查詢不可用 | DB Link 跨庫查詢不支援 LONG |
| 9 | PL/SQL 變數不可宣告 | 無法 DECLARE v_long LONG; |
| 10 | SQL 函數不可用 | SUBSTR、INSTR 等均不支援 |
因限制 #4,需先插入非 LONG 欄位,再逐列更新 LONG 欄位:
DECLARE
CURSOR c_src IS SELECT id, history FROM testlong;
r_src c_src%ROWTYPE;
BEGIN
-- 1. 先複製其他欄位
INSERT INTO testlong1 (id, name)
SELECT id, name FROM testlong;
-- 2. 逐列更新 LONG 欄位
OPEN c_src;
LOOP
FETCH c_src INTO r_src;
EXIT WHEN c_src%NOTFOUND;
UPDATE testlong1 SET history = r_src.history WHERE id = r_src.id;
END LOOP;
CLOSE c_src;
COMMIT;
END;
/
直接 setString 會觸發 ORA-01704(字串字面值過長)。解決方案:使用 PreparedStatement.setCharacterStream 以流式寫入。
String sql = "INSERT INTO msg_info (id, gp_id, title, content, status, create_time, expire_time, upid) VALUES (?,?,?,?, 'C', SYSDATE, SYSDATE+?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setLong(1, msgId);
ps.setInt(2, gpId);
ps.setString(3, title);
// 關鍵:用 CharacterStream 寫入超長文本
ps.setCharacterStream(4, new StringReader(content), content.length());
ps.setInt(5, msgTerm);
ps.setLong(6, upId);
ps.executeUpdate();
conn.commit();
注意:Oracle 12c+ 強烈建議遷移至 CLOB,並使用 setClob / setCharacterStream 對應 CLOB 欄位,相容性更佳。
LONG 型別限制極多,僅作為歷史系統維護之用。新專案請直接採用 CLOB + SecureFiles 儲存,並搭配 Oracle Text 實現全文檢索。若必須操作既有 LONG 欄位,請牢記「先插其他欄、再更新 LONG」與「JDBC 用 CharacterStream」兩大鐵律。