String> 4k에서 작성된 술어에 Oracle clob을 사용하십시오.


11

아래의 Oracle SELECT 술어에 사용하기 위해> 4000 문자 (file_data 바인드 변수에 제공)의 문자열에서 clob를 작성하려고합니다.

myQuery=
select *
from dcr_mols
WHERE flexmatch(ctab,:file_data,'MATCH=ALL')=1;

TO_CLOB () 라운드 file_data를 추가하면 varchar에 대한 악명 높은 Oracle 4k 제한에 실패합니다 (4k 미만의 문자열에는 적합합니다). 오류 (SQL Developer의 경우)는 다음과 같습니다.

ORA-01460: unimplemented or unreasonable conversion requested
01460. 00000 -  "unimplemented or unreasonable conversion requested"

참고 flexmatch 기능은 분자 검색에 사용되며 여기에 설명되어 있습니다. http://help.accelrysonline.com/ulm/onelab/1.0/content/ulm_pdfs/direct/developers/direct_2016_developersguide.pdf

함수 자체는 약간 복잡하지만 본질은 두 번째 매개 변수는 clob이어야합니다. 그래서 내 질문은 4000 자 이상의 Java String bind_variable을 sql (또는 Java)의 clob로 어떻게 변환합니까?

Java (Spring boot 2)에서 아래 방법을 사용하여 clob를 삽입 할 때 작동합니다.

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("file_data", fileDataStr,Types.CLOB);
jdbcNamedParameterTemplate.query(myQuery,parameters,…

이 방법은 작동하지만 FYI 인 수렴 된 flexmatch 오류와 함께 실패합니다.

SQL state [99999]; error code [29902]; ORA-29902: error in executing ODCIIndexStart() routine\nORA-20100: 
MDL-0203: Unable to read from CLOB (csfrm=1, csid=873): 
ORA-22922: nonexistent LOB value\nMDL-0021: Unable to copy LOB to string\nMDL-1051: Molstructure search query is not a valid molecule\nMDL-0976: 
Molecule index search initialization failed\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 329\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 309\n; nested exception is java.sql.SQLException: 
ORA-29902: error in executing ODCIIndexStart() routine\nORA-20100: MDL-0203: Unable to read from CLOB (csfrm=1, csid=873): 
ORA-22922: nonexistent LOB value\nMDL-0021: Unable to copy LOB to string\nMDL-1051: Molstructure search query is not a valid molecule\nMDL-0976: 
Molecule index search initialization failed\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 329\nORA-06512: at \"C$MDLICHEM80.MDL_MXIXMDL\", line 309\n"

참고 SpringBoot 2를 사용하고 있지만 Oracle Connection (Spring NamedParametersJdbcTemplate 객체에서 얻은)을 사용하여 (4 clob <4k에서도) 작동하는 메소드를 얻을 수 없으므로 바보 같은 짓을 한 것으로 의심됩니다. 난 노력 했어:

 @Autowired
 NamedParameterJdbcTemplate  jdbcNamedParameterTemplate;
OracleConnection conn =  this.jdbcNamedParameterTemplate.getJdbcTemplate().getDataSource().getConnection().unwrap(OracleConnection.class);
Clob myClob =  conn.createClob();
myClob.setString( 1, fileDataStr);
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("file_data", myClob,Types.CLOB);

application.properties :

spring.datasource.url=jdbc:oracle:thin:@//${ORA_HOST}:${ORA_PORT}/${ORA_SID}
spring.datasource.username=${ORA_USER}
spring.datasource.password=${ORA_PASS}

오래된 학교에 가서 비 스프링 연결과 setClob () 메소드가있는 PreparedStatement를 사용하면 잘 작동합니다.

OracleDataSource ods = new OracleDataSource();
String url ="jdbc:oracle:thin:@//" + ORA_HOST +":"+ORA_PORT +"/"+ORA_SID;
ods.setURL(url);
ods.setUser(user);
ods.setPassword(passwd);
Connection conn = ods.getConnection();
Clob myClob=conn.createClob();
PreparedStatement ps = conn.prepareStatement("select dcr_number from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1");
myClob.setString(1,myMol);
ps.setClob(1,myClob);
ResultSet rs =ps.executeQuery();

그러나 Java 또는 Sql의 Spring 2 솔루션을 선호합니다. 도움, 제안 부탁드립니다.


이것은 내가 지금까지 한 것과는 다른 좋은 질문 +1입니다. flexmatch()함수에 대한 API 문서를 지적 할 수 있습니까 ? 나는 이것에 대한 필요성을보고 싶다. 솔직히, 나는 WHERE절 에서 큰 값을 매개 변수로 사용하지 않았습니다 . 나는 INSERT그것들을 사용했고를 사용하여 그것들을 검색했다 SELECT. 귀하의 경우는 다릅니다.
팔러

@ The_impaler 질문에있는 문서에 대한 링크가 있습니다. 두려워하는 API는 아니지만 우리가 가진 전부입니다. 이것은 매우 틈새 기능입니다. 분자의 디지털 표현을 검색 할 필요가 있으며이를 위해서는 전문적인 기능이 필요합니다. 즉, 분자는 이미 dcr_mols 테이블에 존재합니다.
DS.

어떤 Oracle 버전을 사용하고 있습니까?
areus

@areaus ojdbc6-11.2.1.0.1
DS.

답변:


5

스트리밍하세요. SQL 문에 큰 값을 붙여 넣을 수는 없습니다.

다음이 필요합니다.

  • INSERTEMPTY_BLOB ()를 사용하여 명령문에 빈 BLOB를 삽입하십시오 .
  • 빈 Blob의 출력 스트림을 가져옵니다.
  • 그런 다음 파일에서 입력 스트림을 가져옵니다. 전체 파일을 메모리에로드하지 마십시오.
  • 그런 다음 버퍼링을 사용하여 입력 스트림에서 출력 스트림으로 블록을 전송하십시오. 16KB 버퍼가해야합니다.
  • 두 스트림을 모두 닫습니다.

이것이 오라클에서 대규모 데이터를 처리하는 표준 방법입니다. 많은 예가 있습니다.

방대한 데이터 BLOBCLOB유형 검색 은 같은 방식으로 작동합니다. 이 경우 InputStream을 사용하십시오.


@The_impaler 클로브를 삽입하지 않았습니다. select
DS

1

"BIOVIA Direct"API의 문서를 읽으면 27 페이지에 흥미로운 예제가 있습니다 (아래 그림 참조).

select ...
from ...
where flexmatch(
ctab,
(select ctab from nostruct_table),
'all'
)=1

이미로드 된 CLOB를 사용합니다 . 따라서 CLOB을 귀하의 테이블에로드하거나 미리 미리로드하는 것이 적절한 해결책이라고 생각합니다.

1 단계-CLOB를 테이블에로드하십시오.

create table mol_file (
  id number(12) primary key not null,
  content clob
);

insert into mol_file (id, content) values (:id, :content);

Java 코드를 사용하여 다른 답변 (인터넷의 많은 예제)에 표시된 것처럼 CLOB 삽입 (아마도 스트림 사용)을 수행하십시오. 예를 들어 ID =로 mol 데이터 내용을 삽입하십시오 123.

2 단계-이미로드 된 mol 파일을 사용하여 쿼리를 실행합니다.

select *
from dcr_mols
WHERE flexmatch(
        ctab,
        (select content from mol_file where id = :id),
        'MATCH=ALL'
      ) = 1;

이전에로드 한 파일 (또는 다른 파일)을 사용 하도록 :id매개 변수를 설정할 수 있습니다 123.


@The_impaler 감사 합니다만, 너트를 깨는 것은 약간의 망치입니다. 실행할 쿼리가 많으므로 코드가 복잡해지고 속도가 느려집니다. 나는 오래된 학교 답변으로 내 질문을 업데이트했는데 더 나은 것이 없다면 마지 못해 사용할 것입니다.
DS.

1

이런 식으로 구식 기능을 호출 할 수 있습니다. ResultSet를 처리 할 클래스를 만듭니다.

 class MyPreparedStatementCallback implements PreparedStatementCallback {
    public Object doInPreparedStatement(PreparedStatement preparedStatement)
            throws SQLException, DataAccessException {
        ResultSet rs = preparedStatement.executeQuery();
        List result = new LinkedList();
        rs.close();
        return result;
    }
}

메소드에서 JdbcTemplate을 사용하여 쿼리를 호출하십시오.

 jdbcTemplate.execute(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection connection)

                throws SQLException, DataAccessException {

            PreparedStatement ps = connection.prepareStatement("select dcr_number from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1");
            Clob myClob =  connection.createClob();
            myClob.setString( 1, fileDataStr);
            MapSqlParameterSource parameters = new MapSqlParameterSource();
            parameters.addValue("file_data", myClob, Types.CLOB);
            ps.setClob(1,myClob);
            return ps;

        };
    }, new MyPreparedStatementCallback());

1

PreparedStatement를 사용하여 되돌려 야했지만 Spring에서 연결을 얻고 Apache Commons BeanListHandler를 사용하여 ResultSet을 객체 List에 매핑하여 일반 구현을 약간 개선했습니다.

import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

@Autowired
NamedParameterJdbcTemplate  jdbcTemplate;

List<MyDao> myMethod(String fileData){
    String myQuery="select * from dcr_mols WHERE flexmatch(ctab,?,'MATCH=ALL')=1";

try {
    Connection conn =  this.jdbcTemplate.getJdbcTemplate().getDataSource().getConnection().unwrap(OracleConnection.class);   // Get connection from spring

    Clob myClob =  conn.createClob();   // Open a dB clob 
    myClob.setString( 1, fileData);     // add data to clob
    PreparedStatement ps = conn.prepareStatement(myQuery);
    ps.setClob(1,myClob);              // Add a clob into the PreparedStatement
    ResultSet rs =ps.executeQuery();   // Execute the prepared statement

    //ResultSetHandler<List<MyDao>> handler = new BeanListHandler<MyDao>(MyDao.class);   // Define the ResultSet handler
    ResultSetHandler<List<MyDao>> handler = new BeanListHandler<MyDao>(MyDao.class, new BasicRowProcessor(new GenerousBeanProcessor()));  // This is better than the above handler , because GenerousBeanProcessor removes the requirement for the column names to exactly match the java variables

    List<MyDao> myDaoList = handler.handle(rs);   // Map ResultSet to List of MyDao objects
    }catch (Exception e) {
        e.printStackTrace();
    }

return myDaoList;
}

0

연결 인 con을 사용하여 fileDataStr을 CLOB로 선언 할 수 있습니다.

java.sql.Clob fileDataStr = oracle.sql.CLOB.createTemporary
(con, false, oracle.sql.CLOB.DURATION_SESSION);

그런 다음 아래처럼 사용하십시오.

 parameters.addValue("file_data", fileDataStr,Types.CLOB);

또한 연결 문자열에서 서비스 이름 대신 SID를 사용하는 경우 아래와 같이 속성 파일을 변경하십시오.

spring.datasource.url=jdbc:oracle:thin:@//${ORA_HOST}:${ORA_PORT}:${ORA_SID}

고마워, 그러나 그것은 매우 어색하다. 데이터는 모든 크기가 될 수 있으므로 덩어리를 만들기 위해 동적 SQL을 사용해야합니다. 또한 clob를 이와 같은 부분으로 나눌 수 있는지 확실하지 않습니다.
DS.

fileDataStr의 종류는 무엇입니까
psaraj12

Java String
DS입니다.

CLOB로 선언하고 java.sql.Clob과 같이 선언 할 수 있습니다. fileDataStr = oracle.sql.CLOB.createTemporary (con, false, oracle.sql.CLOB.DURATION_SESSION);
psaraj12

con이 connection 인 곳에서 fileDataStr을 CLOB로 선언하는 방법에 대한 주석에서 내 예제를 참조하십시오
psaraj12
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.