development

IN 절의 매개 변수 목록이있는 PreparedStatement

big-blog 2020. 9. 24. 08:01
반응형

IN 절의 매개 변수 목록이있는 PreparedStatement [중복]


이 질문에 이미 답변이 있습니다.

쿼리를 실행하는 동안 JDBC의 PreparedStatement에서 in 절에 대한 값을 설정하는 방법.

예:

connection.prepareStatement("Select * from test where field in (?)");

이 절이 여러 값을 보유 할 수 있다면 어떻게 할 수 있습니까? 때로는 매개 변수 목록을 미리 알고 있거나 때로는 미리 알지 못합니다. 이 사건을 어떻게 처리합니까?


내가 할 일은 "?"를 추가하는 것입니다. 가능한 각 값에 대해.

예를 들면 :

List possibleValues = ... 
StringBuilder builder = new StringBuilder();

for( int i = 0 ; i < possibleValue.size(); i++ ) {
    builder.append("?,");
}

String stmt = "select * from test where field in (" 
               + builder.deleteCharAt( builder.length() -1 ).toString() + ")";
PreparedStatement pstmt = ... 

그런 다음 행복하게 매개 변수를 설정합니다.

int index = 1;
for( Object o : possibleValue ) {
   pstmt.setObject(  index++, o ); // or whatever it applies 
}

setArray아래 javadoc에 언급 된 방법을 사용할 수 있습니다 .

http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setArray(int, java.sql.Array)

암호:

PreparedStatement statement = connection.prepareStatement("Select * from test where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"A1", "B2","C3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();

이 링크를 확인할 수 있습니다.

http://www.javaranch.com/journal/200510/Journal200510.jsp#a2

PreparedStatementwith in을 만드는 다양한 방법의 장단점을 설명합니다 .

편집하다:

명백한 접근 방식은 '?'를 동적으로 생성하는 것입니다. 하지만 사용하는 방식에 따라 비효율적 일 수 있기 때문에이 접근 방식 만 제안하고 싶지는 않습니다 (사용할 때 PreparedStatement마다 '컴파일'되어야하기 때문에).


?쿼리에서 임의 개수의 값으로 바꿀 수 없습니다 . 각각 ?은 단일 값에 대한 자리 표시 자입니다. 임의의 수의 값을 지원하려면 ?, ?, ?, ... , ?에서 원하는 값의 수와 동일한 물음표 수를 포함하는 문자열을 동적으로 작성 해야합니다 in.


jdbc4가 필요하면 setArray를 사용할 수 있습니다!

제 경우에는 postgres의 UUID 데이터 유형이 여전히 약점을 가지고있는 것처럼 보이지만 일반적인 유형에서는 작동하지 않았기 때문에 작동하지 않았습니다.

ps.setArray(1, connection.createArrayOf("$VALUETYPE",myValuesAsArray));

물론 $ VALUETYPE 및 myValuesAsArray를 올바른 값으로 대체하십시오.

마크 주석을 따르는 비고 :

데이터베이스와 드라이버가이를 지원해야합니다! Postgres 9.4를 사용해 보았지만 이전에 소개 된 것 같습니다. jdbc 4 드라이버가 필요합니다. 그렇지 않으면 setArray를 사용할 수 없습니다. 스프링 부트와 함께 제공되는 postgresql 9.4-1201-jdbc41 드라이버를 사용했습니다.


당신이 할 수있는 일은 당신이 IN 절 안에 얼마나 많은 값을 넣어야 하는지를 알게되는 즉시 간단한 for 루프에 의해 선택 문자열 ( 'IN (?)'부분)을 동적으로 만드는 것입니다. 그런 다음 PreparedStatement를 인스턴스화 할 수 있습니다.


적어도 IN 절을 사용하는 동적 쿼리와 함께 PreparedStatment를 사용하고 싶지는 않지만 항상 변수가 5 이하이거나 그와 같은 작은 값이지만 그럴지라도 나쁜 생각이라고 생각합니다 (끔찍하지는 않지만 나쁜). 요소의 수가 많을수록 더 나빠질 것입니다 (그리고 끔찍합니다).

IN 절에서 수백 또는 수천 개의 가능성을 상상해보십시오.

  1. 비생산적이며, 새로운 요청이있을 때마다 캐시하기 때문에 성능과 메모리가 손실되며 PreparedStatement는 SQL 주입만을위한 것이 아니라 성능에 관한 것입니다. 이 경우 Statement가 더 좋습니다.

  2. 풀의 한도는 PreparedStatment (-1 기본값이지만 제한해야 함)가 있으며이 한도에 도달합니다! 제한이 없거나 매우 큰 경우 메모리 누수 위험이 있으며 극단적 인 경우 OutofMemory 오류가 발생합니다. 따라서 3 명의 사용자가 사용하는 소규모 개인 프로젝트를위한 것이라면 극적이지는 않지만 대기업에 있고 앱이 수천 명의 사용자와 백만 명의 요청에 의해 사용된다는 것을 원하지는 않습니다.

약간의 독서. IBM : 준비된 명령문 캐싱 사용시 메모리 활용 고려 사항


public static ResultSet getResult(Connection connection, List values) {
    try {
        String queryString = "Select * from table_name where column_name in";

        StringBuilder parameterBuilder = new StringBuilder();
        parameterBuilder.append(" (");
        for (int i = 0; i < values.size(); i++) {
            parameterBuilder.append("?");
            if (values.size() > i + 1) {
                parameterBuilder.append(",");
            }
        }
        parameterBuilder.append(")");

        PreparedStatement statement = connection.prepareStatement(queryString + parameterBuilder);
        for (int i = 1; i < values.size() + 1; i++) {
            statement.setInt(i, (int) values.get(i - 1));
        }

        return statement.executeQuery();
    } catch (Exception d) {
        return null;
    }
}

현재 MySQL은 하나의 메서드 호출에서 여러 값을 설정할 수 없습니다. 그래서 당신은 당신 자신의 통제하에 있어야합니다. 일반적으로 미리 정의 된 매개 변수 수에 대해 하나의 준비된 문을 만든 다음 필요한만큼 배치를 추가합니다.

    int paramSizeInClause = 10; // required to be greater than 0!
    String color = "FF0000"; // red
    String name = "Nathan"; 
    Date now = new Date();
    String[] ids = "15,21,45,48,77,145,158,321,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,358,1284,1587".split(",");

    // Build sql query 
    StringBuilder sql = new StringBuilder();
    sql.append("UPDATE book SET color=? update_by=?, update_date=? WHERE book_id in (");
    // number of max params in IN clause can be modified 
    // to get most efficient combination of number of batches
    // and number of parameters in each batch
    for (int n = 0; n < paramSizeInClause; n++) {
        sql.append("?,");
    }
    if (sql.length() > 0) {
        sql.deleteCharAt(sql.lastIndexOf(","));
    }
    sql.append(")");

    PreparedStatement pstm = null;
    try {
        pstm = connection.prepareStatement(sql.toString());
        int totalIdsToProcess = ids.length;
        int batchLoops = totalIdsToProcess / paramSizeInClause + (totalIdsToProcess % paramSizeInClause > 0 ? 1 : 0);
        for (int l = 0; l < batchLoops; l++) {
            int i = 1;
            pstm.setString(i++, color);
            pstm.setString(i++, name);
            pstm.setTimestamp(i++, new Timestamp(now.getTime()));
            for (int count = 0; count < paramSizeInClause; count++) {
                int param = (l * paramSizeInClause + count);
                if (param < totalIdsToProcess) {
                    pstm.setString(i++, ids[param]);
                } else {
                    pstm.setNull(i++, Types.VARCHAR);
                }
            }
            pstm.addBatch();
        }
    } catch (SQLException e) {
    } finally {
        //close statement(s)
    }

If you don't like to set NULL when no more parameters left, you can modify code to build two queries and two prepared statements. First one is the same, but second statement for the remainder (modulus). In this particular example that would be one query for 10 params and one for 8 params. You will have to add 3 batches for the first query (first 30 params) then one batch for the second query (8 params).


public class Test1 {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("helow");
String where="where task in ";
        where+="(";
    //  where+="'task1'";
        int num[]={1,2,3,4};
        for (int i=0;i<num.length+1;i++) {
            if(i==1){
                where +="'"+i+"'";
            }
            if(i>1 && i<num.length)
                where+=", '"+i+"'";
            if(i==num.length){
                System.out.println("This is last number"+i);
            where+=", '"+i+"')";
            }
        }
        System.out.println(where);  
    }
}

You can use :

for( int i = 0 ; i < listField.size(); i++ ) {
    i < listField.size() - 1 ? request.append("?,") : request.append("?");
}

Then :

int i = 1;
for (String field : listField) {
    statement.setString(i++, field);
}

Exemple :

List<String> listField = new ArrayList<String>();
listField.add("test1");
listField.add("test2");
listField.add("test3");

StringBuilder request = new StringBuilder("SELECT * FROM TABLE WHERE FIELD IN (");

for( int i = 0 ; i < listField.size(); i++ ) {
    request = i < (listField.size() - 1) ? request.append("?,") : request.append("?");
}


DNAPreparedStatement statement = DNAPreparedStatement.newInstance(connection, request.toString);

int i = 1;
for (String field : listField) {
    statement.setString(i++, field);
}

ResultSet rs = statement.executeQuery();

try with this code

 String ids[] = {"182","160","183"};
            StringBuilder builder = new StringBuilder();

            for( int i = 0 ; i < ids.length; i++ ) {
                builder.append("?,");
            }

            String sql = "delete from emp where id in ("+builder.deleteCharAt( builder.length() -1 ).toString()+")";

            PreparedStatement pstmt = connection.prepareStatement(sql);

            for (int i = 1; i <= ids.length; i++) {
                pstmt.setInt(i, Integer.parseInt(ids[i-1]));
            }
            int count = pstmt.executeUpdate();

Many DBs have a concept of a temporary table, even assuming you don't have a temporary table you can always generate one with a unique name and drop it when you are done. While the overhead of creating and dropping a table is large, this may be reasonable for very large operations, or in cases where you are using the database as a local file or in memory (SQLite).

An example from something I am in the middle of (using Java/SqlLite):

String tmptable = "tmp" + UUID.randomUUID();

sql = "create table " + tmptable + "(pagelist text not null)";
cnn.createStatement().execute(sql);

cnn.setAutoCommit(false);
stmt = cnn.prepareStatement("insert into "+tmptable+" values(?);");
for(Object o : rmList){
    Path path = (Path)o;
    stmt.setString(1, path.toString());
    stmt.execute();
}
cnn.commit();
cnn.setAutoCommit(true);

stmt = cnn.prepareStatement(sql);
stmt.execute("delete from filelist where path + page in (select * from "+tmptable+");");
stmt.execute("drop table "+tmptable+");");

Note that the fields used by my table are created dynamically.

This would be even more efficient if you are able to reuse the table.


public static void main(String arg[]) {

    Connection connection = ConnectionManager.getConnection(); 
    PreparedStatement pstmt = null;
          //if the field values are in ArrayList
        List<String> fieldList = new ArrayList();

    try {

        StringBuffer sb = new StringBuffer();  

        sb.append("  SELECT *            \n");
        sb.append("   FROM TEST          \n");
        sb.append("  WHERE FIELD IN (    \n");

        for(int i = 0; i < fieldList.size(); i++) {
            if(i == 0) {
                sb.append("    '"+fieldList.get(i)+"'   \n");
            } else {
                sb.append("   ,'"+fieldList.get(i)+"'   \n");
            }
        }
        sb.append("             )     \n");

        pstmt = connection.prepareStatement(sb.toString());
        pstmt.executeQuery();

    } catch (SQLException se) {
        se.printStackTrace();
    }

}

Using Java 8 APIs, 

    List<Long> empNoList = Arrays.asList(1234, 7678, 2432, 9756556, 3354646);

    List<String> parameters = new ArrayList<>();
    empNoList.forEach(empNo -> parameters.add("?"));   //Use forEach to add required no. of '?'
    String commaSepParameters = String.join(",", parameters); //Use String to join '?' with ','

StringBuilder selectQuery = new StringBuilder().append("SELECT COUNT(EMP_ID) FROM EMPLOYEE WHERE EMP_ID IN (").append(commaSepParameters).append(")");

참고URL : https://stackoverflow.com/questions/3107044/preparedstatement-with-list-of-parameters-in-a-in-clause

반응형