Orcale BD ORA-01704 string literal too long

情境

最近在做一些網路交易系統的開發,過程中需要不斷往 DB 塞入大量的法規 html 字串,取出後在前端再用 v-html 渲染。

範例 SQL

1
2
3
4
5
6
7
8
INSERT INTO DBP.CONTRACT_I18N
(CONTRACT_ID, LOCALE, TITLE, CONTENT)
VALUES (
1,
'zh-TW',
'數位網站<br>登入設施約定條款',
TO_CLOB('<div>超長的法規條款內容</div>'),
);

在 INSERT 的時候 IDE ORA-01704: string literal too long 的訊息;忽然想到以前做專案的時候會在各段落間穿插 ') || TO_CLOB(' 來分段。

原因是 Oracle SQL 單一字串(VARCHAR2 literal) 最大只能 4000 bytes。而在 Oracle SQL Parser(語法解析階段)啟動時,TO_CLOB('...') 內的內容,在進入 TO_CLOB 函數之前,首先被當作 SQL String Literal 處理。所以超過 4000 bytes,Oracle 在 Parser 階段會直接報錯。

如果字串合法(≤ 4000 bytes),Oracle 才會把這個 String Literal 當成參數傳給 TO_CLOB()。

所以就改為:

1
2
3
4
5
6
7
8
INSERT INTO DBP.CONTRACT_I18N
(CONTRACT_ID, LOCALE, TITLE, CONTENT)
VALUES (
1,
'zh-TW',
'數位網站<br>登入設施約定條款',
TO_CLOB('<div>超長的法規條款內容1 ') || TO_CLOB(' 超長的法規條款內容2 ') || TO_CLOB(' 超長的法規條款內容3</div>'),
);

最後乾脆讓 AI 寫一個 Main 方法,之後包在測試工具類裡面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.io.*;

public class ToClobFormatter {
public static void main(String[] args) {
String inputFilePath = "D:/tmp/target.txt";
String outputFilePath = "D:/tmp/result.txt";
int chunkSize = 700; // 以「字數」切段

try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFilePath), "UTF-8"));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFilePath), "UTF-8"))) {

writer.write("TO_CLOB('"); // 開頭先寫入 TO_CLOB
int currentLength = 0;
StringBuilder chunkBuffer = new StringBuilder();

String line;
while ((line = reader.readLine()) != null) {
// 保留原格式與換行,單引號轉義
String processedLine = line.replace("'", "''") + "\n";
int linePos = 0;

while (linePos < processedLine.length()) {
int remaining = chunkSize - currentLength;
int charsToTake = Math.min(remaining, processedLine.length() - linePos);

chunkBuffer.append(processedLine, linePos, linePos + charsToTake);
currentLength += charsToTake;
linePos += charsToTake;

if (currentLength >= chunkSize) {
writer.write(chunkBuffer.toString());
writer.write("')||TO_CLOB('");
chunkBuffer.setLength(0); // 清掉buffer
currentLength = 0;
}
}
}

// 最後剩下的寫出去
if (chunkBuffer.length() > 0) {
writer.write(chunkBuffer.toString());
}
writer.write("')"); // 收尾

} catch (IOException e) {
e.printStackTrace();
}
}
}