¿Cómo obtengo el tamaño de un java.sql.ResultSet?

284

¿No debería ser una operación bastante sencilla? Sin embargo, veo que hay ni una size()ni length()método.

Jake
fuente
11
Me encantaría saber el motivo de esa omisión.
Slamice
Comprendí que la pregunta era que desea encontrar el tamaño del ResultSet IN BYTES, no el número de tuplas ...
DejanLekic
Es muy molesto no tener la dimensión correcta antes de procesar los datos, pero si tiene que almacenarlos en una matriz, puede considerar usar una estructura de datos como Lista y luego convertirlos en una matriz con el método toArray ().
AndreaTaroni86

Respuestas:

270

Haz una SELECT COUNT(*) FROM ...consulta en su lugar.

O

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

En cualquiera de los casos, no tendrá que recorrer todos los datos.

finnw
fuente
8
last () y getRow () no son métodos estáticos en la clase ResultSet.
JeeBee
70
Por razones de brevedad, siempre hago referencia a los métodos de esta manera cuando escribo sobre ellos para otros, independientemente de si son estáticos o no. En realidad, crear una instancia del objeto y llamar al método está implícito.
laz
51
Escribo SomeClass.staticMethod () y SomeClass # instanceMethod () para menos confusión.
Jake
9
¿Cómo se obtiene el valor devuelto al ejecutar a select count?
Naftuli Kay
16
ResultSet#last()no funciona en todo tipo de ResultSetobjetos, debe asegurarse de usar uno que sea 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
}
JeeBee
fuente
55
Dentro del bloque de código if (rs.last ()), ¿no sería el método correcto rs.beforeFirst () en lugar de rs.first ()? De esta manera, no se saltará el primer registro en su conjunto de resultados para procesarlo en el ciclo while.
karlgrz
¿No te olvidas de volver a colocar el cursor antes de Primero fuera del bloque if?
Gobliins
Como dicen los documentos de ResultSet , getRow()funciona para TYPE_FORWARD_ONLYResultSets y beforeFirst()arroja errores para ellos. ¿No es esta respuesta defectuosa entonces?
CodePro_NotYet
55
Esto solo funciona cuando la declaración se crea con la opción insensible al desplazamiento:ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
BullyWiiPlaza
19

Bueno, si tiene un ResultSettipo ResultSet.TYPE_FORWARD_ONLY, desea mantenerlo de esa manera (y no cambiar a uno ResultSet.TYPE_SCROLL_INSENSITIVEo ResultSet.TYPE_SCROLL_INSENSITIVEpara poder usarlo .last()).

Sugiero un hack muy agradable y eficiente, donde agregas una primera fila falsa / falsa en la parte superior que contiene el número de filas.

Ejemplo

Digamos que su consulta es la siguiente

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

y su salida se ve como

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]

Simplemente refactorice su código a algo como esto:

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

Su salida de consulta ahora será algo así como

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]

Entonces solo tienes que

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
fuente
Interesante, pero ¿cómo generaría dinámica / genéricamente las primeras declaraciones select: cast (null as boolean) as MYBOOL, ect? Para eso, necesitará metadatos de los campos y tipos de datos de la declaración "select", como boolean, char, int, ect ...) que pueden requerir un viaje adicional a la base de datos que negará todos los beneficios.
user1697575
Esto es útil cuando tiene acceso a todos los detalles del campo y la velocidad es su principal preocupación (y, por lo tanto, debe mantenerse rápido ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi
11
int i = 0;
while(rs.next()) {
    i++;
}
bhaskar
fuente
No entiendo cuál es el inconveniente de usar este método para calcular el tamaño de ResultSet. Esto es genial ... no se usa un parámetro SQL adicional. Por favor comente sobre este método.
Madeyedexter
55
El rendimiento es la palabra clave aquí. Imagine que su conjunto de resultados es de 100 millones de registros, entonces verá el problema
Pierre
77
Quiero saber el tamaño del conjunto de resultados ANTES de procesar los resultados porque necesito hacer una matriz del mismo tamaño de antemano. Y, como se señaló en otras respuestas, escanear todas las filas dos veces no siempre funcionará.
Ivo
11

Tengo una excepción cuando uso rs.last()

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

:

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

es debido a que es por defecto ResultSet.TYPE_FORWARD_ONLY, lo que significa que solo puedes usarrs.next()

la solucion es:

stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY); 
Dan
fuente
12
Cambiar de ResultSet.TYPE_FORWARD_ONLYa ResultSet.TYPE_SCROLL_INSENSITIVEgeneralmente conlleva una gran penalización de rendimiento.
Unai Vivi
3
Lo probé en mi mesa (10 columnas, 187 392 filas). Mi prueba hizo consultas y cargó todos los elementos a la cadena. Para TYPE_FORWARD_ONLY tardó aproximadamente 1 segundo. Para TYPE_SCROLL_INSENSITIVE tomó aproximadamente 7 segundos. Cuando lo usé SELECT COUNT(*) FROM default_tblantes SELECT COUNT(*) FROM default_tbl, tardé menos de 1,5 segundos. Probé en la base de datos de derby incorporada 10.11.1.1
Vit Bernatik
4

La forma de obtener el tamaño de ResultSet, sin necesidad de usar ArrayList, etc.

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

Ahora obtendrá el tamaño, y si desea imprimir el ResultSet, antes de imprimir use también la siguiente línea de código,

rs.beforeFirst();  
Anptk
fuente
4

[Consideración de velocidad]

Gran cantidad de personas aquí sugiere, ResultSet.last()pero para eso necesitaría abrir la conexión, ya ResultSet.TYPE_SCROLL_INSENSITIVEque para la base de datos incrustada Derby es hasta 10 veces MÁS LENTO que ResultSet.TYPE_FORWARD_ONLY.

Según mis micropruebas para las bases de datos Derby y H2 integradas, es mucho más rápido llamar SELECT COUNT(*)antes de su SELECT.

Aquí está con más detalle mi código y mis puntos de referencia

Vit Bernatik
fuente
3

Es una forma sencilla de hacer recuento de filas.

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
fuente
44
Si, eso funciona. Pero creo que el OP tiene dificultades para conocer el número de filas antes de procesarlas realmente. Razones de la vida real que tendría que luchar contra este problema hasta el momento: 1.) paginación de filas de registro 2.) que muestra las filas procesadas en las tareas de ejecución prolongada con fines de monitoreo del progreso ...
ppeterka
La asignación previa del tamaño de la estructura de datos es otra razón. He visto muchas bibliotecas que devuelven listas de 10 elementos cuando solo hay un valor único porque los desarrolladores tuvieron este mismo problema con ResultSet.
Joseph Lust el
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
fuente
1

Verifiqué el valor de tiempo de ejecución de la interfaz ResultSet y descubrí que era casi un ResultSetImpl todo el tiempo. ResultSetImpl tiene un método llamado getUpdateCount()que devuelve el valor que está buscando.

Esta muestra de código debería ser suficiente:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Me doy cuenta de que el downcasting generalmente es un procedimiento inseguro, pero este método aún no me ha fallado.

clausavram
fuente
2
No funciona con Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo
1

Hoy, utilicé esta lógica por la que no sé cómo obtener el recuento de 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
fuente
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
fuente
0

Estaba teniendo el mismo problema. Utilizando ResultSet.first()de esta manera justo después de que la ejecución lo resolvió:

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

Documentación ( enlace ):

boolean first()
    throws SQLException

Mueve el cursor a la primera fila de este ResultSetobjeto.

Devoluciones:

truesi el cursor está en una fila válida; falsesi no hay filas en el conjunto de resultados

Lanza

SQLException- si se produce un error de acceso a la base de datos; Este método se llama en un conjunto de resultados cerrado o el tipo de conjunto de resultados esTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - si el controlador JDBC no admite este método

Ya que:

1,2

Israel Hernández
fuente
0

Enfoque más fácil, consulta Run Count (*), haz resultSet.next () para apuntar a la primera fila y luego haz resultSet.getString (1) para obtener el conteo. Código:

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

Dale un nombre a la columna.

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

Haga referencia a esa columna del objeto ResultSet en un int y haga su lógica desde allí.

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
fuente