以下语句在 Oracle 18c 上不起作用,但在 Oracle 23ai 上运行良好:
try (Statement s = connection.createStatement()) {
try (ResultSet rs = s.executeQuery(
"""
SELECT (SELECT to_clob('123412341234') x FROM dual)
FROM dual
CONNECT BY LEVEL <= 20
"""
)) {
while (rs.next()) {
System.out.println(rs.getString(1));
}
}
}
成功获取前 10 行后,出现以下错误:
123412341234
123412341234
123412341234
123412341234
123412341234
123412341234
123412341234
123412341234
123412341234
123412341234
ORA-22922: 不存在的 LOB 值
它似乎并不严格与 ojdbc 相关,因为我也可以在 SQL*Plus 中重现类似的问题:
SQL> SELECT (SELECT to_clob('123412341234') x FROM dual)
2 FROM dual
3 CONNECT BY LEVEL <= 11;
(SELECTTO_CLOB('123412341234')XFROMDUAL)
--------------------------------------------------------------------------------
123412341234
ERROR:
ORA-22922: nonexistent LOB value
这是一个已知的错误吗?如何解决它?
虽然我不知道这个错误本身(它一定是,没有理由不工作,显然,它已在 23ai 中修复),但这里有一些解决方法,知道这与标量子查询有关:
CLOB
此查询没有暴露这样的问题:
SELECT (SELECT to_clob('123412341234') x FROM dual) || to_clob('')
FROM dual
CONNECT BY LEVEL <= 20
to_clob()
函数调用中这似乎也有效:
SELECT to_clob((SELECT to_clob('123412341234') x FROM dual))
FROM dual
CONNECT BY LEVEL <= 20
这可行,但只是将问题推迟到后面的一行:
try (Statement s = connection.createStatement()) {
s.setFetchSize(1000);
try (ResultSet rs = s.executeQuery(
"""
SELECT (SELECT to_clob('123412341234') x FROM dual)
FROM dual
CONNECT BY LEVEL <= 20
"""
)) {
while (rs.next()) {
System.out.println(rs.getString(1));
}
}
}
Clob
值,并延迟 Clob.free()
调用此技术类似于手动规避获取大小对查询行为的影响,分配
Clob
资源,并延迟释放资源直到查询执行结束:
try (Statement s = connection.createStatement()) {
try (ResultSet rs = s.executeQuery(
"""
SELECT (SELECT to_clob('123412341234') x FROM dual)
FROM dual
CONNECT BY LEVEL <= 20
"""
)) {
List<Clob> clobs = new ArrayList<>();
while (rs.next()) {
Clob clob = rs.getClob(1);
clobs.add(clob);
System.out.println(clob.getSubString(1, (int) clob.length()));
}
for (Clob clob : clobs)
clob.free();
}
}
提前释放
Clob
值似乎又会带来问题。
Clob
释放拥有与 JDBC 获取大小大小完全相同的未释放
Clob
值的缓冲区似乎也可以工作。这似乎比上述将大量数据缓存在 ojdbc 中的方法更好:
try (Statement s = connection.createStatement()) {
try (ResultSet rs = s.executeQuery(
"""
SELECT (SELECT to_clob('123412341234') x FROM dual)
FROM dual
CONNECT BY LEVEL <= 100
"""
)) {
Deque<Clob> clobs = new ArrayDeque<>();
while (rs.next()) {
Clob clob = rs.getClob(1);
clobs.add(clob);
System.out.println(clob.getSubString(1, (int) clob.length()));
int size = clobs.size() - s.getFetchSize();
while (size --> 0)
clobs.pollFirst().free();
}
for (Clob clob : clobs)
clob.free();
}
}