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 = ...
а потом счастливо установить параметры
int index = 1; for( Object o : possibleValue ) { pstmt.setObject( index++, o ); // or whatever it applies }
вы могли бы использовать
setArray
метод, как указано в Javadoc ниже:код:
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:
это контрпродуктивно, вы потеряли производительность и память, потому что вы кэшируете каждый время нового запроса и PreparedStatement предназначены не только для SQL-инъекции, но и для производительности. В этом случае заявление лучше.
У вашего пула есть предел 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();
многие базы данных имеют понятие временной таблицы, даже если у вас нет временной таблицы, вы всегда можете создать ее с уникальным именем и удалить ее, когда вы закончите. Хотя накладные расходы на создание и удаление таблицы велики, это может быть разумно для очень больших операций или в тех случаях, когда вы используете базу данных в качестве локального файла или в памяти (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(")");