Тип данных Oracle JDBC и Oracle CHAR

У меня есть сложная проблема с обработкой драйвером JDBC Oracle CHAR типов данных. Возьмем простую таблицу:

create table x (c char(4));
insert into x (c) values ('a');  -- inserts 'a   '

Поэтому, когда я что-то вставляю в CHAR(4), строка всегда заполняется пробелами. Это также делается, когда я выполняю такие запросы:

select * from x where c = 'a';    -- selects 1 record
select * from x where c = 'a ';   -- selects 1 record
select * from x where c = 'a   '; -- selects 1 record

Здесь константа 'a' также заполнена пробелами. Вот почему запись всегда возвращается. Это верно, когда эти запросы также выполняются с использованием JDBC PreparedStatement. Теперь самое сложное, когда я хочу использовать переменную связывания:

PreparedStatement stmt = 
  conn.prepareStatement("select * from x where c = ?");
stmt.setString(1, "a");    // This won't return any records
stmt.setString(1, "a   "); // This will return a record
stmt.executeQuery();

Это обходной путь:

PreparedStatement stmt = 
  conn.prepareStatement("select * from x where trim(c) = trim(?)");
stmt.setString(1, "a");    // This will return a record
stmt.setString(1, "a   "); // This will return a record
stmt.executeQuery();

EDIT: Теперь это ограничения:

  • Приведенный выше обходной путь нежелателен, так как он изменяет содержимое как c, так и ?, И делает использование индексов для c довольно сложным.
  • Перемещение столбца с CHAR на VARCHAR (как и должно быть, конечно) невозможно

EDIT: причины этих ограничений в том, что я задаю этот вопрос с точки зрения разработчика jOOQ, библиотека абстракции базы данных. Итак, мои требования состоят в том, чтобы предоставить очень универсальное решение, которое ничего не ломает в клиентском коде jOOQ. Вот почему я не большой поклонник обходного пути. И именно поэтому у меня нет доступа к объявлению этого столбца CHAR. Но все же, я хочу быть в состоянии справиться с этим делом.

Что бы вы сделали вместо этого? Что является хорошей практикой для обработки типов данных CHAR, когда я хочу игнорировать завершающие пробелы?


person Lukas Eder    schedule 16.03.2011    source источник
comment
Несмотря на то, что вы заявляете, что переход на VARCHAR невозможен (почему?), VARCHAR — единственное разумное решение для ваших требований.   -  person a_horse_with_no_name    schedule 17.03.2011
comment
Я с тобой согласен. поясню в своем вопросе   -  person Lukas Eder    schedule 17.03.2011


Ответы (6)


Если хочешь

stmt.setString(1, "a");    // This won't return any records

чтобы вернуть запись, попробуйте

conn.prepareStatement("select * from x where c = cast(? as char(4))")
person Gary Myers    schedule 16.03.2011
comment
PS. Я ненавижу CHAR как тип данных. - person Gary Myers; 17.03.2011
comment
Я их тоже ненавижу. Похоже, что они в некоторой степени способны обеспечить функциональность переменной длины, но всегда есть подводные камни. - person Lukas Eder; 17.03.2011
comment
Хм, я не подумал преобразовать правую сторону в CHAR(4). С другой стороны, мне нужно знать точную длину столбца (проблема в том, что X и C в моем примере являются произвольными заполнителями... - person Lukas Eder; 17.03.2011

Я не вижу причин использовать тип данных CHAR, даже если это char(1) в Oracle. Можете ли вы вместо этого изменить тип данных?

person Piyush Mattoo    schedule 17.03.2011
comment
К сожалению, это не вариант, так как это не моя база данных. Моя логика - это просто абстракция базы данных... Я соответствующим образом обновлю вопрос. - person Lukas Eder; 17.03.2011
comment
CHAR может быть несколько полезен для кодов, таких как коды страны/языка/валюты ISO и т. д. Но я согласен с вами, так как даже в этом случае это не имеет большого значения. Вы также можете использовать VARCHAR. - person Lukas Eder; 17.03.2011

Решение Гэри работает хорошо. Вот альтернатива.

Если вы используете драйвер Oracle JDBC, вызов prepareStatement() фактически вернет OraclePreparedStatement, у которого есть метод setFixedCHAR(), который автоматически дополняет ваши входные данные пробелами.

String sql = "select * from x where c = ?";
OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement(sql);
stmt.setFixedCHAR(1, "a");
...

Очевидно, что приведение типов безопасно только в том случае, если вы используете драйвер Oracle.

Единственная причина, по которой я бы посоветовал вам использовать это вместо ответа Гэри, заключается в том, что вы можете изменить размеры столбцов без необходимости изменять код JDBC. Драйвер заполняет правильное количество пробелов, при этом разработчику не нужно знать/управлять размером столбца.

person RustyTheBoyRobot    schedule 18.11.2015

У меня есть хорошее решение для этого. Вы должны добавить одно свойство при получении соединения с базой данных.

NLS_LANG=american_america.AL32UTF8

или в соединении Java вы можете использовать следующий код:

java.util.Properties info = new java.util.Properties();
info.put ("user", user);
info.put ("password",password);
info.put("fixedString","TRUE");
info.put("NLS_LANG","american_america.AL32UTF8");
info.put("SetBigStringTryClob","TRUE");
String url="jdbc:oracle:thin:@"+serverName;
log.debug("url="+url);
log.debug("info="+info);
Class.forName("oracle.jdbc.OracleDriver");
conn  = DriverManager.getConnection(url,info);
person swapna ailineni    schedule 05.03.2018

другой способ - изменить ваш sql как

select * from x where NVL(TRIM(c),' ') = NVL(TRIM('a'),' ')
person 張僑文    schedule 02.11.2018
comment
Привет @張僑文 и добро пожаловать в Stack Overflow. Это не совсем полный ответ. Попробуйте объяснить, ПОЧЕМУ вы можете написать SQL таким образом и ПОЧЕМУ это будет выгодно. - person cmprogram; 02.11.2018

Просто добавьте RTRIM() к имени столбца (которое определено) в запросе на обновление.

person Dilan H. Ranawaka    schedule 22.02.2021