Следующий оператор не работает в 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
Это известная ошибка? Как это обойти?
🤔 А знаете ли вы, что...
Java обеспечивает возможность создания пользовательских классов и библиотек.
Хотя я не знаю о самой ошибке (должно быть, нет причин, по которым она не работает, и, по-видимому, она была исправлена в 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
на размер выборкиНаличие буфера невысвобожденных значений Clob
, точно такого же размера, как размер выборки JDBC, также, похоже, работает. Кажется, это лучше, чем описанные выше подходы, при которых тонны данных кэшируются в 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();
}
}