Come posso ottenere le dimensioni di un java.sql.ResultSet?


285

Non dovrebbe essere un'operazione piuttosto semplice? Tuttavia, vedo che non esiste né un metodo size()length().


11
Mi piacerebbe sapere il motivo di tale omissione.
Slamice

La mia comprensione della domanda era che volevi trovare la dimensione del ResultSet IN BYTES, non il numero di tuple ...
DejanLekic,

È molto fastidioso non avere la giusta dimensione prima di elaborare i dati, ma se è necessario memorizzarli in un array, è possibile prendere in considerazione l'utilizzo di una struttura di dati come List e quindi convertirli in un array con il metodo toArray ().
Andrea Taroni,

Risposte:


270

SELECT COUNT(*) FROM ...Esegui invece una query.

O

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

In entrambi i casi, non sarà necessario eseguire il ciclo su tutti i dati.


8
last () e getRow () non sono metodi statici nella classe ResultSet.
JeeBee,

70
Per brevità, faccio sempre riferimento ai metodi in questo modo quando ne scrivo ad altri, indipendentemente dal fatto che siano statici o meno. In realtà, è implicita la creazione di un'istanza dell'oggetto e la chiamata del metodo.
Laz

51
Scrivo SomeClass.staticMethod () e SomeClass # instanceMethod () per meno confusione.
Jake,

9
Come si recupera il valore restituito quando si esegue un select count?
Naftuli Kay,

16
ResultSet#last()non funziona su tutti i tipi di ResultSetoggetti, è necessario assicurarsi di utilizzarne uno che sia ResultSet.TYPE_SCROLL_INSENSITIVEoResultSet.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
}

5
All'interno del blocco di codice if (rs.last ()), il metodo corretto non sarebbe rs.beforeFirst () invece di rs.first ()? In questo modo, non si sta saltando il primo record nel set di risultati per l'elaborazione nel ciclo while.
karlgrz,

non dimenticare di reimpostare il cursore su Prima prima del blocco if?
Gobliins,

Come dicono i documenti ResultSet , getRow()funziona per i TYPE_FORWARD_ONLYResultSet e beforeFirst()genera errori per quelli. Questa risposta non è difettosa allora?
CodePro_NotYet

5
Funziona solo quando l'istruzione viene creata con l'opzione insensibile allo scorrimento:ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
BullyWiiPlaza,

19

Bene, se hai un ResultSettipo ResultSet.TYPE_FORWARD_ONLY, vuoi mantenerlo in quel modo (e non passare a un ResultSet.TYPE_SCROLL_INSENSITIVEo ResultSet.TYPE_SCROLL_INSENSITIVEper poterlo usare .last()).

Suggerisco un hack molto bello ed efficiente, in cui aggiungi una prima riga fasulla / fasulla in alto contenente il numero di righe.

Esempio

Supponiamo che la tua query sia la seguente

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

e il tuo output sembra

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]

Rifatti semplicemente il tuo codice su qualcosa del genere:

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

L'output della query ora sarà simile

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]

Quindi devi solo

if(rs.next())
    System.out.println("Recordcount: "+rs.getInt("RECORDCOUNT"));//hack: first record contains the record count
while(rs.next())
    //do your stuff

Interessante, ma come genereresti dinamicamente / genericamente le prime istruzioni select: cast (null come booleano) come MYBOOL, ect? Per questo avrai bisogno di metadati dei campi e dei tipi di dati dell'istruzione "select", come boolean, char, int, ect ...) che potrebbero richiedere un viaggio DB aggiuntivo che annullerà tutti i vantaggi.
user1697575

Questo è utile quando hai accesso a tutti i dettagli del campo e la velocità è la tua principale preoccupazione (e quindi devi restare con un digiuno ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi,

11
int i = 0;
while(rs.next()) {
    i++;
}

Non capisco qual è lo svantaggio di utilizzare questo metodo per calcolare la dimensione di ResultSet. Questo è fantastico ... nessun uso di un parametro SQL aggiuntivo. Si prega di commentare questo metodo.
Madeyedexter,

5
Performance è la parola chiave qui. Immagina che il tuo set di risultati sia record da 100 milioni, quindi vedrai il problema
Pierre,

7
Voglio conoscere la dimensione del set di risultati PRIMA di elaborare i risultati perché devo creare in anticipo un array della stessa dimensione. E, come notato in altre risposte, la scansione di tutte le righe due volte non funzionerà sempre.
Ivo,

11

Ho ricevuto un'eccezione durante l'utilizzo rs.last()

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

:

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

per impostazione predefinita è ResultSet.TYPE_FORWARD_ONLY, il che significa che è possibile utilizzare solors.next()

la soluzione è:

stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY); 

12
Il passaggio da ResultSet.TYPE_FORWARD_ONLYa di ResultSet.TYPE_SCROLL_INSENSITIVEsolito comporta una penalità di prestazione enorme .
Unai Vivi,

3
L'ho provato sul mio tavolo (10 colonne, 187 392 righe). Il mio test ha eseguito una query e caricato tutti gli elementi nella stringa. Per TYPE_FORWARD_ONLY ci sono voluti circa 1 secondo. Per TYPE_SCROLL_INSENSITIVE ci sono voluti circa 7 secondi. Quando l'ho usato piuttosto SELECT COUNT(*) FROM default_tblprima SELECT COUNT(*) FROM default_tblci sono voluti complessivamente meno di 1,5 secondi. Ho testato il database derby incorporato 10.11.1.1
Vit Bernatik,

4

Il modo di ottenere dimensioni di ResultSet, Non è necessario utilizzare ArrayList ecc

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

Ora otterrai le dimensioni e, se vuoi stampare il ResultSet, prima di stampare usa anche la seguente riga di codice,

rs.beforeFirst();  

4

[Considerazione sulla velocità]

Molti ppl qui suggeriscono, ResultSet.last()ma per questo sarebbe necessario aprire la connessione in quanto ResultSet.TYPE_SCROLL_INSENSITIVEper il database incorporato Derby è fino a 10 volte più LENTO di ResultSet.TYPE_FORWARD_ONLY.

Secondo i miei micro-test per database Derby e H2 incorporati, è molto più veloce chiamare SELECT COUNT(*)prima di SELECT.

Ecco più in dettaglio il mio codice e i miei parametri di riferimento


3

È un modo semplice per eseguire il conteggio delle righe.

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

4
Sì, funziona. Ma penso che l'OP abbia difficoltà a conoscere il numero di righe prima di elaborarle effettivamente. Ragioni della vita reale Finora avrei dovuto combattere questo problema: 1.) paging delle righe dei record 2.) che mostra le righe elaborate in attività di lunga durata ai fini del monitoraggio dei progressi ...
ppeterka

La preallocazione delle dimensioni della struttura dei dati è un'altra ragione. Ho visto un sacco di librerie restituire liste di 10 elementi quando c'è un solo valore perché gli sviluppatori hanno avuto lo stesso problema con 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);

1

Ho controllato il valore di runtime dell'interfaccia ResultSet e ho scoperto che era praticamente un ResultSetImpl per tutto il tempo. ResultSetImpl ha un metodo chiamato getUpdateCount()che restituisce il valore che stai cercando.

Questo esempio di codice dovrebbe essere sufficiente:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Mi rendo conto che il downcasting è generalmente una procedura non sicura, ma questo metodo non mi ha ancora deluso.


2
Non funziona con Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo,

1

Oggi ho usato questa logica perché non so come ottenere il conteggio di 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.

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

0

Avevo lo stesso problema. Utilizzando ResultSet.first()in questo modo subito dopo l'esecuzione risolto:

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

Documentazione ( link ):

boolean first()
    throws SQLException

Sposta il cursore sulla prima riga di questo ResultSetoggetto.

Ritorna:

truese il cursore si trova su una riga valida; falsese non ci sono righe nel set di risultati

Produce:

SQLException- se si verifica un errore di accesso al database; questo metodo viene chiamato su un set di risultati chiuso o il tipo di set di risultati èTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - se il driver JDBC non supporta questo metodo

Da:

1.2


0

Approccio più semplice, eseguire la query Count (*), eseguire resultSet.next () per puntare alla prima riga e quindi eseguire resultSet.getString (1) per ottenere il conteggio. Codice :

ResultSet rs = statement.executeQuery("Select Count(*) from your_db");
if(rs.next()) {
   int count = rs.getString(1).toInt()
}

-1

Dai un nome alla colonna ..

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

Fai riferimento a quella colonna dall'oggetto ResultSet in un int e fai la tua logica da lì.

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.");
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.