PreparedStatement со списком параметров в предложении IN [дубликат]

этот вопрос уже есть ответ здесь:

Как установить значение для предложения in в preparedStatement в JDBC при выполнении запроса.

пример:

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

Если это предложение in может содержать несколько значений, как я могу это сделать. Иногда Я знаю список параметров заранее или иногда я не знаю заранее. Как вести это дело?

15 ответов


что я делаю, чтобы добавить"?"для каждого возможного значения.

для instace:

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 = ... 

а затем счастливо установить params

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.Матрица)

код:

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

это объясняет плюсы и минусы различных методов создания PreparedStatement с in предложения.

EDIT:

очевидный подход состоит в том, чтобы динамически генерировать '?"часть во время выполнения, но я не хочу просто предлагать только этот подход, потому что в зависимости от того, как вы его используете, он может быть неэффективным (поскольку PreparedStatement нужно будет "компилировать" каждый раз, когда он будет использоваться)


вы не можете заменить ? в вашем запросе с произвольным количеством значений. Каждый ? - Это только одно значение. Чтобы поддерживать произвольное количество значений, вам придется динамически создавать строку, содержащую ?, ?, ?, ... , ? С количеством вопросительных знаков, совпадающих с количеством значений, которые вы хотите в вашем in предложения.


вам нужен jdbc4, тогда вы можете использовать setArray!

в моем случае это не сработало, так как тип данных UUID в postgres, похоже, все еще имеет свои слабые места, но для обычных типов он работает.

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

конечно, замените $VALUETYPE и myValuesAsArray правильными значениями.

Примечание следующие метки комментарий:

ваша база данных и драйвер должны поддерживать это! Я пробовал Postgres 9.4, но я думаю, что это было введено ранее. Вам нужен драйвер jdbc 4, иначе setArray будет недоступен. Я использовал драйвер postgresql 9.4-1201-jdbc41, который поставляется с spring boot


Что вы можете сделать, это динамически построить строку выбора ('IN (?) 'часть) простым циклом for, как только вы знаете, сколько значений вам нужно поместить в предложение IN. Затем можно создать экземпляр PreparedStatement.


вы не хотите использовать PreparedStatment с динамическими запросами, используя предложение IN, по крайней мере, вы уверены, что всегда находитесь под переменной 5 или небольшим значением, но даже так, я думаю, что это плохая идея ( не ужасно, но плохо ). Поскольку количество элементов велико, то будет хуже ( и страшнее ).

представьте себе сто или тысячу возможностей в вашем предложении IN:

  1. это контрпродуктивно, вы потеряли производительность и память, потому что вы кэшируете каждый время нового запроса и PreparedStatement предназначены не только для SQL-инъекции, но и для производительности. В этом случае утверждение лучше.

  2. У вашего пула есть предел PreparedStatment (-1 defaut, но вы должны ограничить его), и вы достигнете этого предела ! и если у вас нет предела или очень большого предела, у вас есть некоторый риск утечки памяти, и в крайнем случае ошибки 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)
    }

Если вы не хотите устанавливать NULL, когда больше не осталось параметров, вы можете изменить код для создания двух запросов и двух подготовленных операторов. Первое-то же самое, но второе утверждение для остатка (модуля). В этом частности пример: один запрос для 10 параметров и один для 8 параметров. Вам нужно будет добавить 3 пакета для первого запроса (первые 30 параметров), затем один пакет для второго запроса (8 параметров).


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);  
    }
}

вы можете использовать :

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

затем :

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();

попробуйте с этим кодом

 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();

многие DBs имеют концепцию временной таблицы, даже если у вас нет временной таблицы, вы всегда можете создать ее с уникальным именем и удалить ее, когда закончите. Хотя накладные расходы на создание и удаление таблицы велики, это может быть разумно для очень больших операций или в случаях, когда вы используете базу данных в качестве локального файла или в памяти (SQLite).

пример из того, что я нахожусь в середине (используя 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+");");

Примечание. что поля моей таблицы создаются динамически.

Это было бы еще более эффективным, если вы можете повторно использовать таблицы.


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(")");