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

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

Пример:

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

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

Ответ 1

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

Для 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 = ... 

И затем счастливо установите параметры

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

Ответ 2

Вы можете использовать метод 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();

Ответ 3

Вы можете проверить эту ссылку:

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

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

EDIT:

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

Ответ 7

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

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

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

  • У вашего пула есть префикс PreparedStatment (-1 defaut, но вы должны его ограничить), и вы достигнете этого предела! и если у вас нет предела или очень большого предела, у вас есть некоторый риск утечки памяти и в крайнем случае ошибки OutofMemory. Поэтому, если это для вашего небольшого персонального проекта, используемого тремя пользователями, это не драматично, но вы не хотите, чтобы, если вы находитесь в большой компании и что приложение используется тысячами человек и миллионом запросов.

Некоторые чтения. IBM: соображения использования памяти при использовании подготовленного кэширования инструкций

Ответ 8

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

Ответ 9

У многих БД есть концепция временной таблицы, даже если у вас нет временной таблицы, вы всегда можете генерировать ее с уникальным именем и отбрасывать ее, когда вы закончите. Хотя накладные расходы на создание и удаление таблицы велики, это может быть разумно для очень больших операций или в тех случаях, когда вы используете базу данных как локальный файл или в памяти (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+");");

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

Ответ 10

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

Ответ 11

В настоящее время 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 параметров).

Ответ 12

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

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

Пример:

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

Ответ 13

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

}

Ответ 14

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

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

Ответ 15

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