Разве это не должно быть довольно простой операцией? Однако я вижу, что нет ни метода size(), ни length().
Я понял вопрос, что вы хотите найти размер ResultSet IN BYTES, а не количество кортежей ...
Очень неприятно иметь неправильное измерение перед обработкой данных, но если вам нужно сохранить их в массиве, вы можете рассмотреть возможность использования структуры данных, такой как List, а затем преобразовать их в массив с помощью метода toArray ().




ResultSet rs = ps.executeQuery();
int rowcount = 0;
if (rs.last()) {
rowcount = rs.getRow();
rs.beforeFirst(); // not rs.first() because the rs.next() below will move on, missing the first element
}
while (rs.next()) {
// do your standard per row stuff
}
Разве внутри блока кода if (rs.last ()) не будет правильным методом rs.beforeFirst () вместо rs.first ()? Таким образом, вы не пропускаете первую запись в наборе результатов для обработки в цикле while.
не забудьте снова установить курсор на beforeFirst вне блока if?
Как сказано в Документы ResultSet, getRow() работает с TYPE_FORWARD_ONLY ResultSets, а beforeFirst() выдает для них ошибки. Разве это не ошибочный ответ?
Это работает только в том случае, если оператор создан с параметром нечувствительности к прокрутке: ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
Вместо этого выполните запрос SELECT COUNT(*) FROM ....
ИЛИ ЖЕ
int size =0;
if (rs != null)
{
rs.last(); // moves cursor to the last row
size = rs.getRow(); // get row id
}
В любом случае вам не придется перебирать все данные в цикле.
last () и getRow () не являются статическими методами в классе ResultSet.
Для краткости я всегда ссылаюсь на методы таким образом, когда пишу о них другим, независимо от того, статичны они или нет. Фактически подразумевается создание экземпляра объекта и вызов метода.
Я пишу SomeClass.staticMethod () и SomeClass # instanceMethod () для меньшей путаницы.
Как получить значение, возвращаемое при выполнении select count?
@TK Kocheran, точно так же, как вы получите результат любого запроса с одной строкой или одним столбцом, с executeQuery(), next() и getInt(1)
ResultSet#last() не работает со всеми типами объектов ResultSet, вам необходимо убедиться, что вы используете тот, который является ResultSet.TYPE_SCROLL_INSENSITIVE или ResultSet.TYPE_SCROLL_SENSITIVE.
Что, если набор результатов возвращается из вызова хранимой процедуры? Нет возможности заранее узнать его размер?
@Giodude, SELECT COUNT(*) нужно будет выполнить внутри хранимой процедуры, или хранимая процедура может вернуть временное представление, и вызывающий может выполнить SELECT COUNT(*) для этого.
Что делать, если вы не можете изменить сохраненную процедуру, потому что она пришла откуда-то еще?
@Giodude, в этом случае невозможно эффективно подсчитать строки. Но если вам нужно вручную подсчитать строки, я бы сделал это на стороне сервера, если это возможно (например, с помощью временной процедуры).
Кто-нибудь знает, почему так сложно подсчитать набор результатов? Почему они просто не включили метод ResultSet#size() в API?
@ryvantage Немного поздно. Они этого не делают, потому что счетчик заранее не известен, и только после материализации всего набора результатов база данных узнает, сколько строк она создала. Это неэффективно, поскольку материализация всех строк требует памяти, ввода-вывода и времени обработки. При чтении набора результатов, предназначенного только для пересылки (а иногда и наборов результатов с возможностью прокрутки), база данных будет читать строки только по запросу (и может читать немного вперед). Таким образом, клиент может обрабатывать некоторые строки, в то время как база данных читает больше: эффективно и с меньшим объемом памяти.
Значит ли это, что select count(*) from table - операция O (n)?
Кроме того, другие ответы указали на использование ResultSet::last() как способ найти счет. Будет ли тогда метод last() O (n) операцией?
@ryvantage: Да. В MS SQL Server, например, ResultSet::last() - это O (N) с курсором только вперед (по умолчанию). Вы можете получить O (1) last (с точки зрения клиента) с прокручиваемым + статическим курсором. Но для этих типов курсоров требуется, чтобы сервер сохранял в памяти весь набор ключей. Сервер заранее не знает, что вы собираетесь выбросить результаты и использовать только количество строк. Он должен построить план запроса с предположением, что вы потребляете все данные.
Этот ответ косвенно предполагает, что когда вы собираетесь использовать данные впоследствии, то ResultSet.last() является оптимальным решением. Фактически, измерения скорости, которые я сделал для встроенных баз данных Derby и H2, показывают, что даже когда вы используете данные после этого, все равно быстрее просто запросить базу данных SELECT COUNT(*) до этого, а затем получить результат в режиме ResultSet.TYPE_FORWARD_ONLY.
Это простой способ подсчета строк.
ResultSet rs = job.getSearchedResult(stmt);
int rsCount = 0;
//but notice that you'll only get correct ResultSet size after end of the while loop
while(rs.next())
{
//do your other per row stuff
rsCount = rsCount + 1;
}//end while
Да, это работает. Но я думаю, что OP изо всех сил пытается знать, сколько строк до фактически их обрабатывает. Причины из реальной жизни, по которым мне пришлось бы бороться с этой проблемой: 1.) разбиение на страницы строк записей 2.) отображение строк, обработанных в длительных задачах, для целей мониторинга прогресса ...
Еще одна причина - это предварительное выделение размера структуры данных. Я видел множество библиотек, возвращающих 10 списков элементов, когда есть только одно значение, потому что у разработчика была такая же проблема с ResultSet.
У меня исключение при использовании rs.last()
if (rs.last()){
rowCount = rs.getRow();
rs.beforeFirst();
}
:
java.sql.SQLException: Invalid operation for forward only resultset
это связано с тем, что по умолчанию это ResultSet.TYPE_FORWARD_ONLY, что означает, что вы можете использовать только rs.next()
решение:
stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
Переключение с ResultSet.TYPE_FORWARD_ONLY на ResultSet.TYPE_SCROLL_INSENSITIVE обычно влечет за собой снижение производительности огромный.
Я проверил его на своей таблице (10 столбцов, 187 392 строки). Мой тест запросил и загрузил все элементы в строку. Для TYPE_FORWARD_ONLY это заняло около 1 секунды. Для TYPE_SCROLL_INSENSITIVE это заняло около 7 секунд. Когда я использовал SELECT COUNT(*) FROM default_tbl, а не SELECT COUNT(*) FROM default_tbl, это заняло в целом менее 1,5 секунды. Я тестировал встроенную базу данных derby 10.11.1.1
theStatement=theConnection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet theResult=theStatement.executeQuery(query);
//Get the size of the data returned
theResult.last();
int size = theResult.getRow() * theResult.getMetaData().getColumnCount();
theResult.beforeFirst();
Что ж, если у вас есть ResultSet типа ResultSet.TYPE_FORWARD_ONLY, вы хотите оставить его таким (и нет, чтобы переключиться на ResultSet.TYPE_SCROLL_INSENSITIVE или ResultSet.TYPE_SCROLL_INSENSITIVE, чтобы иметь возможность использовать .last()).
Я предлагаю очень хороший и эффективный прием, когда вы добавляете первую фальшивую / фальшивую строку вверху, содержащую количество строк.
Пример
Допустим, ваш запрос следующий
select MYBOOL,MYINT,MYCHAR,MYSMALLINT,MYVARCHAR
from MYTABLE
where ...blahblah...
и ваш результат выглядит как
true 65537 "Hey" -32768 "The quick brown fox"
false 123456 "Sup" 300 "The lazy dog"
false -123123 "Yo" 0 "Go ahead and jump"
false 3 "EVH" 456 "Might as well jump"
...
[1000 total rows]
Просто реорганизуйте свой код примерно так:
Statement s=myConnection.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
String from_where = "FROM myTable WHERE ...blahblah... ";
//h4x
ResultSet rs=s.executeQuery("select count(*)as RECORDCOUNT,"
+ "cast(null as boolean)as MYBOOL,"
+ "cast(null as int)as MYINT,"
+ "cast(null as char(1))as MYCHAR,"
+ "cast(null as smallint)as MYSMALLINT,"
+ "cast(null as varchar(1))as MYVARCHAR "
+from_where
+"UNION ALL "//the "ALL" part prevents internal re-sorting to prevent duplicates (and we do not want that)
+"select cast(null as int)as RECORDCOUNT,"
+ "MYBOOL,MYINT,MYCHAR,MYSMALLINT,MYVARCHAR "
+from_where);
Результат вашего запроса теперь будет примерно таким:
1000 null null null null null
null true 65537 "Hey" -32768 "The quick brown fox"
null false 123456 "Sup" 300 "The lazy dog"
null false -123123 "Yo" 0 "Go ahead and jump"
null false 3 "EVH" 456 "Might as well jump"
...
[1001 total rows]
Так что тебе просто нужно
if (rs.next())
System.out.println("Recordcount: "+rs.getInt("RECORDCOUNT"));//hack: first record contains the record count
while(rs.next())
//do your stuff
Интересно, но как бы вы динамически / в общем генерировали первые операторы выбора: cast (null как boolean) как MYBOOL и т. д.? Для этого вам понадобятся метаданные полей и типов данных оператора select, например boolean, char, int, ect ...), которые могут потребовать дополнительной поездки к БД, которая сведет на нет все преимущества.
Это полезно, когда у вас есть доступ ко всем деталям поля и скорость является вашей основной заботой (и, следовательно, вам нужно придерживаться быстрого ResultSet.TYPE_FORWARD_ONLY)
Я проверил значение времени выполнения интерфейса ResultSet и обнаружил, что это почти все время ResultSetImpl. ResultSetImpl имеет метод getUpdateCount(), который возвращает искомое значение.
Этого примера кода должно хватить:ResultSet resultSet = executeQuery(sqlQuery);double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()
Я понимаю, что понижающее преобразование - вообще небезопасная процедура, но этот метод меня еще не подвел.
Не работает с Tomcat / MySQL: java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
String sql = "select count(*) from message";
ps = cn.prepareStatement(sql);
rs = ps.executeQuery();
int rowCount = 0;
while(rs.next()) {
rowCount = Integer.parseInt(rs.getString("count(*)"));
System.out.println(Integer.parseInt(rs.getString("count(*)")));
}
System.out.println("Count : " + rowCount);
int i = 0;
while(rs.next()) {
i++;
}
Я не понимаю, в чем недостаток использования этого метода для расчета размера ResultSet. Это здорово ... никакого дополнительного параметра SQL. Прокомментируйте, пожалуйста, этот метод.
Ключевое слово здесь - производительность. Представьте, что ваш набор результатов составляет 100 миллионов записей, тогда вы увидите проблему
Я хочу знать размер набора результатов ПЕРЕД обработкой результатов, потому что мне нужно заранее создать массив того же размера. И, как отмечалось в других ответах, сканирование всех строк дважды не всегда сработает.
@Ivo не могли бы вы использовать список вместо массива из-за заметного снижения производительности?
@ jones-chris Кто знает, это было 3 года назад, я понятия не имею, что делал. Я ненавижу массивы, поэтому предполагаю, что использование списка невозможно. В любом случае массив должен быть более производительным, чем список (если методы списка не будут оптимизированы средой выполнения).
Способ получения размера ResultSet, нет необходимости использовать ArrayList и т. д.
int size =0;
if (rs != null)
{
rs.beforeFirst();
rs.last();
size = rs.getRow();
}
Теперь вы получите размер, и если вы хотите распечатать ResultSet, перед печатью также используйте следующую строку кода,
rs.beforeFirst();
У меня была такая же проблема. Использование ResultSet.first() таким образом сразу после того, как выполнение решило эту проблему:
if (rs.first()){
// Do your job
} else {
// No rows take some actions
}
Документация (связь):
boolean first() throws SQLExceptionMoves the cursor to the first row in this
ResultSetobject.Returns:
trueif the cursor is on a valid row;falseif there are no rows in the result setThrows:
SQLException- if a database access error occurs; this method is called on a closed result set or the result set type isTYPE_FORWARD_ONLY
SQLFeatureNotSupportedException- if the JDBC driver does not support this methodSince:
1.2
[Рассмотрение скорости]
Многие люди здесь предлагают ResultSet.last(), но для этого вам нужно будет открыть соединение как ResultSet.TYPE_SCROLL_INSENSITIVE, который для встроенной базы данных Derby в 10 раз превышает ПОМЕДЛЕННЕЕ, чем ResultSet.TYPE_FORWARD_ONLY.
Согласно моим микротестам для встроенных баз данных Derby и H2, гораздо быстрее вызвать SELECT COUNT(*) перед SELECT.
Сегодня я использовал эту логику, почему я не знаю, как рассчитать RS.
int chkSize = 0;
if (rs.next()) {
do { ..... blah blah
enter code here for each rs.
chkSize++;
} while (rs.next());
} else {
enter code here for rs size = 0
}
// good luck to u.
Дайте столбцу имя ..
String query = "SELECT COUNT(*) as count FROM
Ссылайтесь на этот столбец из объекта ResultSet в int и выполняйте свою логику оттуда.
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, item.getProductId());
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
int count = resultSet.getInt("count");
if (count >= 1) {
System.out.println("Product ID already exists.");
} else {
System.out.println("New Product ID.");
}
}
Самый простой подход, запрос Run Count (*), сделайте resultSet.next (), чтобы указать на первую строку, а затем просто выполните resultSet.getString (1), чтобы получить счет. Код:
ResultSet rs = statement.executeQuery("Select Count(*) from your_db");
if (rs.next()) {
int count = rs.getString(1).toInt()
}
Я хотел бы знать причину этого упущения.