Jak uzyskać rozmiar java.sql.ResultSet?

285

Czy nie powinna to być dość prosta operacja? Widzę jednak, że nie ma metody size()nor length().

Jake
źródło
11
Chciałbym poznać przyczynę tego pominięcia.
Slamice,
Zrozumiałem pytanie, że chcesz znaleźć rozmiar zestawu wyników w bajtach, a nie liczbę krotek ...
DejanLekic
Bardzo denerwujące jest to, że nie ma właściwego wymiaru przed przetwarzaniem danych, ale jeśli musisz przechowywać je w tablicy, możesz rozważyć użycie struktury danych takiej jak List, a następnie przekonwertować je na tablicę za pomocą metody toArray ().
AndreaTaroni86

Odpowiedzi:

270

SELECT COUNT(*) FROM ...Zamiast tego wykonaj zapytanie.

LUB

int size =0;
if (rs != null) 
{
  rs.last();    // moves cursor to the last row
  size = rs.getRow(); // get row id 
}

W obu przypadkach nie będziesz musiał zapętlać całych danych.

finnw
źródło
8
last () i getRow () nie są metodami statycznymi w klasie ResultSet.
JeeBee,
70
Dla zwięzłości zawsze piszę o metodach w ten sposób, pisząc o nich innym, bez względu na to, czy są one statyczne, czy nie. W rzeczywistości zakłada się utworzenie instancji obiektu i wywołanie metody.
laz
51
Piszę SomeClass.staticMethod () i SomeClass # instanceMethod () dla mniejszego zamieszania.
Jake
9
Jak pobrać wartość zwróconą podczas wykonywania select count?
Naftuli Kay
16
ResultSet#last()nie działa na wszystkich typach ResultSetobiektów, musisz upewnić się, że używasz jednego z nich ResultSet.TYPE_SCROLL_INSENSITIVElubResultSet.TYPE_SCROLL_SENSITIVE
Marius Ion
91
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
}
JeeBee
źródło
5
Czy w bloku kodu if (rs.last ()) poprawną metodą nie byłoby rs.beforeFirst () zamiast rs.first ()? W ten sposób nie pomijasz pierwszego rekordu w zestawie wyników do przetwarzania w pętli while.
karlgrz
nie zapomnij ustawić z powrotem kursora na pozycję Pierwsze na zewnątrz bloku if?
Gobliins,
Jak mówią dokumenty ResultSet , getRow()działa dla TYPE_FORWARD_ONLYResultSets i beforeFirst()zgłasza dla nich błędy. Czy więc ta odpowiedź nie jest błędna?
CodePro_NotYet
5
Działa to tylko wtedy, gdy instrukcja jest tworzona z opcją niewrażliwą na przewijanie:ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
BullyWiiPlaza
19

Cóż, jeśli masz ResultSettypu ResultSet.TYPE_FORWARD_ONLYchcesz zachować to w ten sposób (i nie przełączyć się na ResultSet.TYPE_SCROLL_INSENSITIVElub ResultSet.TYPE_SCROLL_INSENSITIVEw celu umożliwienia użytkowania .last()).

Sugeruję bardzo fajny i wydajny hack, w którym dodajesz pierwszy fałszywy / fałszywy wiersz na górze zawierający liczbę wierszy.

Przykład

Powiedzmy, że twoje zapytanie jest następujące

select MYBOOL,MYINT,MYCHAR,MYSMALLINT,MYVARCHAR
from MYTABLE
where ...blahblah...

a twój wygląd wygląda

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]

Po prostu popraw kod na coś takiego:

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

Dane wyjściowe zapytania będą teraz podobne

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]

Musisz po prostu

if(rs.next())
    System.out.println("Recordcount: "+rs.getInt("RECORDCOUNT"));//hack: first record contains the record count
while(rs.next())
    //do your stuff
Unai Vivi
źródło
Ciekawe, ale jak byś generował dynamicznie / generalnie pierwsze instrukcje select: cast (null as boolean) jako MYBOOL, ect? W tym celu potrzebujesz metadanych pól i typów danych instrukcji „select”, takich jak boolean, char, int, ect ...), które mogą wymagać dodatkowej podróży DB, która neguje wszystkie korzyści.
user1697575,
Jest to przydatne, gdy masz dostęp do wszystkich szczegółów pola, a najważniejsza jest prędkość (i dlatego musisz trzymać się postu ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi
11
int i = 0;
while(rs.next()) {
    i++;
}
Bhaskar
źródło
Nie rozumiem, jaka jest wada używania tej metody do obliczania rozmiaru zestawu wyników. To wspaniale ... bez użycia dodatkowego parametru SQL. Proszę skomentować tę metodę.
Madeyedexter
5
Wydajność jest tutaj słowem kluczowym. Wyobraź sobie, że Twój zestaw wyników to 100 milionów rekordów, a wtedy zobaczysz problem
Pierre,
7
Chcę poznać rozmiar zestawu wyników PRZED przetworzeniem wyników, ponieważ muszę wcześniej utworzyć tablicę o tym samym rozmiarze. I, jak zauważono w innych odpowiedziach, skanowanie wszystkich wierszy dwa razy nie zawsze będzie działać.
Ivo
11

Mam wyjątek podczas używania rs.last()

if(rs.last()){
    rowCount = rs.getRow(); 
    rs.beforeFirst();
}

:

java.sql.SQLException: Invalid operation for forward only resultset

jest to spowodowane domyślnie ResultSet.TYPE_FORWARD_ONLY, co oznacza, że ​​możesz używać tylkors.next()

rozwiązaniem jest:

stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY); 
Dan
źródło
12
Przełączanie z ResultSet.TYPE_FORWARD_ONLYna ResultSet.TYPE_SCROLL_INSENSITIVEogół pociąga za sobą w wielkim spadku wydajności.
Unai Vivi
3
Testowałem to na moim stole (10 kolumn, 187 392 wierszy). Mój test wykonał zapytanie i załadował wszystkie elementy do łańcucha. Dla TYPE_FORWARD_ONLY zajęło to około 1 sekundy. Dla TYPE_SCROLL_INSENSITIVE zajęło to około 7 sekund. Kiedy użyłem SELECT COUNT(*) FROM default_tblwcześniej SELECT COUNT(*) FROM default_tbl, zajęło to mniej niż 1,5 sekundy. Testowałem na wbudowanej bazie danych derby 10.11.1.1
Vit Bernatik
4

Sposób uzyskania rozmiaru ResultSet, nie ma potrzeby używania ArrayList itp

int size =0;  
if (rs != null)   
{  
rs.beforeFirst();  
 rs.last();  
size = rs.getRow();
}

Teraz otrzymasz rozmiar, a jeśli chcesz wydrukować zestaw wyników, przed wydrukowaniem użyj również następującego wiersza kodu,

rs.beforeFirst();  
Anptk
źródło
4

[Uwzględnienie prędkości]

Wiele ppl tutaj sugeruje, ResultSet.last()ale do tego trzeba by otworzyć połączenie, ponieważ ResultSet.TYPE_SCROLL_INSENSITIVEdla wbudowanej bazy danych Derby jest do 10 razy wolniejsze niż ResultSet.TYPE_FORWARD_ONLY.

Według moich mikrotestów dla osadzonych baz danych Derby i H2 znacznie szybciej jest zadzwonić SELECT COUNT(*)przed SELECT.

Oto bardziej szczegółowo mój kod i moje testy porównawcze

Vit Bernatik
źródło
3

Jest to prosty sposób na liczenie wierszy.

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
CounterSpell
źródło
4
Tak, to działa. Ale myślę, że OP boryka się ze znajomością liczby wierszy, zanim faktycznie je przetworzy. Prawdziwe powody, dla których musiałbym do tej pory walczyć z tym problemem: 1.) stronicowanie rekordowych wierszy 2.) pokazywanie wierszy przetworzonych w długo trwających zadaniach do celów monitorowania postępów ...
ppeterka
Innym powodem jest wstępne przydzielanie wielkości struktury danych. Widziałem, że wiele bibliotek zwraca 10 list Element, gdy jest tylko jedna wartość, ponieważ deweloper miał ten sam problem z ResultSet.
Joseph Lust
2
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);
Peter.Chu
źródło
1

Sprawdziłem wartość środowiska uruchomieniowego interfejsu ResultSet i odkryłem, że przez cały czas była to w zasadzie ResultSetImpl . ResultSetImpl ma wywoływaną metodę, getUpdateCount()która zwraca poszukiwaną wartość.

Ten przykładowy kod powinien wystarczyć:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Zdaję sobie sprawę, że downcasting jest ogólnie niebezpieczną procedurą, ale ta metoda jeszcze mnie nie zawiodła.

clausavram
źródło
2
Nie działa z Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo
1

Dzisiaj użyłem tej logiki, dlaczego nie wiem, jak uzyskać liczbę 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.
parksangdonews
źródło
0
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();
Ben
źródło
0

Miałem ten sam problem. Używając ResultSet.first()w ten sposób zaraz po rozwiązaniu wykonania:

if(rs.first()){
    // Do your job
} else {
    // No rows take some actions
}

Dokumentacja ( link ):

boolean first()
    throws SQLException

Przenosi kursor do pierwszego wiersza w tym ResultSetobiekcie.

Zwroty:

truejeśli kursor znajduje się w poprawnym wierszu; falsejeśli nie ma wierszy w zestawie wyników

Rzuty:

SQLException- jeśli wystąpi błąd dostępu do bazy danych; ta metoda jest wywoływana dla zamkniętego zestawu wyników lub typem zestawu wyników jestTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - jeśli sterownik JDBC nie obsługuje tej metody

Od:

1.2

Israel Hernández
źródło
0

Najłatwiejsze podejście, zapytanie Run Count (*), wykonaj resultSet.next (), aby wskazać pierwszy wiersz, a następnie po prostu wykonaj wynikSet.getString (1), aby uzyskać liczbę. Kod :

ResultSet rs = statement.executeQuery("Select Count(*) from your_db");
if(rs.next()) {
   int count = rs.getString(1).toInt()
}
użytkownik7120462
źródło
-1

Nadaj kolumnie nazwę…

String query = "SELECT COUNT(*) as count FROM

Odwołaj się do tej kolumny z obiektu ResultSet do int i wykonaj swoją logikę stamtąd.

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.");
    }
}
ReMaX
źródło