Как мне получить размер java.sql.ResultSet?

Разве это не должно быть довольно простой операцией? Однако я вижу, что нет ни метода size(), ни length().

Я хотел бы знать причину этого упущения.

Slamice 04.03.2012 10:53

Я понял вопрос, что вы хотите найти размер ResultSet IN BYTES, а не количество кортежей ...

DejanLekic 06.12.2012 21:40

Очень неприятно иметь неправильное измерение перед обработкой данных, но если вам нужно сохранить их в массиве, вы можете рассмотреть возможность использования структуры данных, такой как List, а затем преобразовать их в массив с помощью метода toArray ().

AndreaTaroni86 04.09.2019 12:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
295
3
481 624
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

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.

karlgrz 26.01.2009 20:04

не забудьте снова установить курсор на beforeFirst вне блока if?

Gobliins 13.01.2015 14:43

Как сказано в Документы ResultSet, getRow() работает с TYPE_FORWARD_ONLY ResultSets, а beforeFirst() выдает для них ошибки. Разве это не ошибочный ответ?

CodePro_NotYet 20.07.2016 03:08

Это работает только в том случае, если оператор создан с параметром нечувствительности к прокрутке: ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

BullyWiiPlaza 21.02.2017 16:55
Ответ принят как подходящий

Вместо этого выполните запрос 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.

JeeBee 10.10.2008 20:21

Для краткости я всегда ссылаюсь на методы таким образом, когда пишу о них другим, независимо от того, статичны они или нет. Фактически подразумевается создание экземпляра объекта и вызов метода.

laz 10.10.2008 22:23

Я пишу SomeClass.staticMethod () и SomeClass # instanceMethod () для меньшей путаницы.

Jake 13.05.2011 07:17

Как получить значение, возвращаемое при выполнении select count?

Naftuli Kay 02.06.2011 13:14

@TK Kocheran, точно так же, как вы получите результат любого запроса с одной строкой или одним столбцом, с executeQuery(), next() и getInt(1)

finnw 05.06.2011 14:41

ResultSet#last() не работает со всеми типами объектов ResultSet, вам необходимо убедиться, что вы используете тот, который является ResultSet.TYPE_SCROLL_INSENSITIVE или ResultSet.TYPE_SCROLL_SENSITIVE.

Marius Ion 13.06.2012 17:47

Что, если набор результатов возвращается из вызова хранимой процедуры? Нет возможности заранее узнать его размер?

Giovanni Botta 22.05.2013 19:07

@Giodude, SELECT COUNT(*) нужно будет выполнить внутри хранимой процедуры, или хранимая процедура может вернуть временное представление, и вызывающий может выполнить SELECT COUNT(*) для этого.

finnw 22.05.2013 20:19

Что делать, если вы не можете изменить сохраненную процедуру, потому что она пришла откуда-то еще?

Giovanni Botta 23.05.2013 23:48

@Giodude, в этом случае невозможно эффективно подсчитать строки. Но если вам нужно вручную подсчитать строки, я бы сделал это на стороне сервера, если это возможно (например, с помощью временной процедуры).

finnw 24.05.2013 10:42

Кто-нибудь знает, почему так сложно подсчитать набор результатов? Почему они просто не включили метод ResultSet#size() в API?

ryvantage 14.12.2013 00:48

@ryvantage Немного поздно. Они этого не делают, потому что счетчик заранее не известен, и только после материализации всего набора результатов база данных узнает, сколько строк она создала. Это неэффективно, поскольку материализация всех строк требует памяти, ввода-вывода и времени обработки. При чтении набора результатов, предназначенного только для пересылки (а иногда и наборов результатов с возможностью прокрутки), база данных будет читать строки только по запросу (и может читать немного вперед). Таким образом, клиент может обрабатывать некоторые строки, в то время как база данных читает больше: эффективно и с меньшим объемом памяти.

Mark Rotteveel 25.08.2014 14:56

Значит ли это, что select count(*) from table - операция O (n)?

ryvantage 25.08.2014 15:00

Кроме того, другие ответы указали на использование ResultSet::last() как способ найти счет. Будет ли тогда метод last() O (n) операцией?

ryvantage 25.08.2014 22:59

@ryvantage: Да. В MS SQL Server, например, ResultSet::last() - это O (N) с курсором только вперед (по умолчанию). Вы можете получить O (1) last (с точки зрения клиента) с прокручиваемым + статическим курсором. Но для этих типов курсоров требуется, чтобы сервер сохранял в памяти весь набор ключей. Сервер заранее не знает, что вы собираетесь выбросить результаты и использовать только количество строк. Он должен построить план запроса с предположением, что вы потребляете все данные.

finnw 25.08.2014 23:24

Этот ответ косвенно предполагает, что когда вы собираетесь использовать данные впоследствии, то ResultSet.last() является оптимальным решением. Фактически, измерения скорости, которые я сделал для встроенных баз данных Derby и H2, показывают, что даже когда вы используете данные после этого, все равно быстрее просто запросить базу данных SELECT COUNT(*) до этого, а затем получить результат в режиме ResultSet.TYPE_FORWARD_ONLY.

Vit Bernatik 22.05.2015 00:29

Это простой способ подсчета строк.

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.) отображение строк, обработанных в длительных задачах, для целей мониторинга прогресса ...

ppeterka 24.05.2013 11:23

Еще одна причина - это предварительное выделение размера структуры данных. Я видел множество библиотек, возвращающих 10 списков элементов, когда есть только одно значение, потому что у разработчика была такая же проблема с ResultSet.

Joseph Lust 21.11.2013 21:24

У меня исключение при использовании 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 обычно влечет за собой снижение производительности огромный.

Unai Vivi 24.05.2013 11:58

Я проверил его на своей таблице (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

Vit Bernatik 19.05.2015 15:07

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 ...), которые могут потребовать дополнительной поездки к БД, которая сведет на нет все преимущества.

user1697575 31.03.2016 17:25

Это полезно, когда у вас есть доступ ко всем деталям поля и скорость является вашей основной заботой (и, следовательно, вам нужно придерживаться быстрого ResultSet.TYPE_FORWARD_ONLY)

Unai Vivi 22.05.2017 00:56

Я проверил значение времени выполнения интерфейса 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

Panu Haaramo 18.06.2014 14:31

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. Прокомментируйте, пожалуйста, этот метод.

Madeyedexter 13.03.2015 08:21

Ключевое слово здесь - производительность. Представьте, что ваш набор результатов составляет 100 миллионов записей, тогда вы увидите проблему

Pierre 13.05.2016 19:49

Я хочу знать размер набора результатов ПЕРЕД обработкой результатов, потому что мне нужно заранее создать массив того же размера. И, как отмечалось в других ответах, сканирование всех строк дважды не всегда сработает.

Ivo 11.01.2017 01:10

@Ivo не могли бы вы использовать список вместо массива из-за заметного снижения производительности?

jones-chris 16.09.2020 23:27

@ jones-chris Кто знает, это было 3 года назад, я понятия не имею, что делал. Я ненавижу массивы, поэтому предполагаю, что использование списка невозможно. В любом случае массив должен быть более производительным, чем список (если методы списка не будут оптимизированы средой выполнения).

Ivo 18.09.2020 00:42

Способ получения размера 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 SQLException

Moves the cursor to the first row in this ResultSet object.

Returns:

true if the cursor is on a valid row; false if there are no rows in the result set

Throws:

SQLException - if a database access error occurs; this method is called on a closed result set or the result set type is TYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - if the JDBC driver does not support this method

Since:

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

Другие вопросы по теме