Comment obtenir la taille d'un jeu java.sql.ResultSet?


285

Cela ne devrait-il pas être une opération assez simple? Cependant, je vois qu'il n'y a size()ni length()méthode ni .


11
J'adorerais connaître la raison de cette omission.
Slamice

Ma compréhension de la question était que vous vouliez trouver la taille du ResultSet IN BYTES, pas le nombre de tuples ...
DejanLekic

Il est très ennuyeux de ne pas avoir la bonne dimension avant de traiter les données, mais si vous devez les stocker dans un tableau, vous pouvez envisager d'utiliser une structure de données comme List, puis les convertir en un tableau avec la méthode toArray ().
AndreaTaroni86

Réponses:


270

Faites SELECT COUNT(*) FROM ...plutôt une requête.

OU

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

Dans les deux cas, vous n'aurez pas à parcourir toutes les données.


8
last () et getRow () ne sont pas des méthodes statiques dans la classe ResultSet.
JeeBee

70
Par souci de concision, je fais toujours référence aux méthodes de cette façon lorsque j'écris à leur sujet aux autres, qu'elles soient statiques ou non. La création d'une instance de l'objet et l'appel de la méthode sont implicites.
laz

51
J'écris SomeClass.staticMethod () et SomeClass # instanceMethod () pour moins de confusion.
Jake

9
Comment récupérer la valeur retournée lors de l'exécution d'un select count?
Naftuli Kay

16
ResultSet#last()ne fonctionne pas sur tous les types d' ResultSetobjets, vous devez vous assurer d' utiliser celui qui est soit ResultSet.TYPE_SCROLL_INSENSITIVEouResultSet.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
Dans le bloc de code if (rs.last ()), la bonne méthode ne serait-elle pas rs.beforeFirst () au lieu de rs.first ()? De cette façon, vous ne sautez pas le premier enregistrement de votre jeu de résultats pour le traitement dans la boucle while.
karlgrz le

n'oubliez pas de remettre le curseur sur beforeFirst en dehors du bloc if?
Gobliins

Comme le disent les documents ResultSet , getRow()fonctionne pour TYPE_FORWARD_ONLYResultSets et beforeFirst()génère des erreurs pour ceux-ci. Cette réponse n'est-elle pas défectueuse alors?
CodePro_NotYet

5
Cela ne fonctionne que lorsque l'instruction est créée avec l'option de défilement insensible:ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
BullyWiiPlaza

19

Eh bien, si vous avez un ResultSettype, ResultSet.TYPE_FORWARD_ONLYvous devez le conserver de cette façon (et ne pas passer à un ResultSet.TYPE_SCROLL_INSENSITIVEou ResultSet.TYPE_SCROLL_INSENSITIVEpour pouvoir l'utiliser .last()).

Je suggère un hack très agréable et efficace, où vous ajoutez une première ligne fausse / bidon en haut contenant le nombre de lignes.

Exemple

Disons que votre requête est la suivante

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

et votre sortie ressemble

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]

Réorganisez simplement votre code en quelque chose comme ceci:

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

La sortie de votre requête sera maintenant quelque chose comme

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]

Il vous suffit donc de

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

Intéressant, mais comment généreriez-vous dynamiquement / génériquement les premières instructions select: cast (null comme booléen) en MYBOOL, ect? Pour cela, vous aurez besoin de métadonnées des champs et des types de données de l'instruction "select", comme booléen, char, int, ect ...) qui pourraient nécessiter un voyage DB supplémentaire qui annulera tous les avantages.
user1697575

Ceci est utile lorsque vous avez accès à tous les détails du terrain et que la vitesse est votre principale préoccupation (et que vous devez donc vous en tenir à un jeûne ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi

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

Je ne comprends pas quel est l'inconvénient d'utiliser cette méthode pour calculer la taille de ResultSet. C'est super ... pas d'utilisation d'un paramètre SQL supplémentaire. Veuillez commenter cette méthode.
Madeyedexter

5
La performance est le mot clé ici. Imaginez que votre jeu de résultats compte 100 millions d'enregistrements, vous verrez le problème
Pierre

7
Je veux connaître la taille du jeu de résultats AVANT de traiter les résultats, car je dois au préalable créer un tableau de la même taille. Et, comme indiqué dans d'autres réponses, l'analyse de toutes les lignes deux fois ne fonctionnera pas toujours.
Ivo

11

J'ai une exception lors de l'utilisation rs.last()

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

:

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

c'est dû par défaut à ce qu'il est ResultSet.TYPE_FORWARD_ONLY, ce qui signifie que vous ne pouvez utiliser quers.next()

la solution est:

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

12
Passer de ResultSet.TYPE_FORWARD_ONLYà ResultSet.TYPE_SCROLL_INSENSITIVEentraîne généralement une énorme pénalité de performance.
Unai Vivi

3
Je l'ai testé sur ma table (10 colonnes, 187 392 lignes). Mon test a interrogé et chargé tous les éléments dans la chaîne. Pour TYPE_FORWARD_ONLY, cela a pris environ 1 seconde. Pour TYPE_SCROLL_INSENSITIVE, il a fallu environ 7 secondes. Quand je l'ai utilisé plutôt SELECT COUNT(*) FROM default_tblavant, SELECT COUNT(*) FROM default_tblil a fallu au total moins de 1,5 seconde. J'ai testé sur la base de données derby intégrée 10.11.1.1
Vit Bernatik

4

La façon d'obtenir la taille de ResultSet, pas besoin d'utiliser ArrayList, etc.

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

Maintenant, vous obtiendrez la taille, et si vous voulez imprimer le ResultSet, avant d'imprimer, utilisez également la ligne de code suivante,

rs.beforeFirst();  

4

[Considération de vitesse]

Beaucoup de ppl suggèrent ici, ResultSet.last()mais pour cela, vous devez ouvrir une connexion ResultSet.TYPE_SCROLL_INSENSITIVEqui, pour la base de données intégrée Derby, est jusqu'à 10 fois plus lente queResultSet.TYPE_FORWARD_ONLY .

Selon mes micro-tests pour les bases de données Derby et H2 intégrées, il est beaucoup plus rapide d'appeler SELECT COUNT(*)avant votre SELECT.

Voici plus en détail mon code et mes benchmarks


3

C'est un moyen simple de compter les lignes.

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
Ouais, ça marche. Mais je pense que l'OP a du mal à connaître le nombre de lignes avant de les traiter. Des raisons réelles pour lesquelles je devrais lutter contre ce problème jusqu'à présent: 1.) pagination des lignes d'enregistrement 2.) montrant les lignes traitées dans les tâches de longue durée à des fins de suivi des progrès ...
ppeterka

La préallocation de la taille de la structure de données est une autre raison. J'ai vu beaucoup de bibliothèques renvoyer 10 listes d'éléments lorsqu'il n'y a qu'une seule valeur car les développeurs ont eu le même problème avec 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

J'ai vérifié la valeur d'exécution de l' interface ResultSet et j'ai découvert que c'était à peu près tout le temps un ResultSetImpl . ResultSetImpl a une méthode appeléegetUpdateCount() qui renvoie la valeur que vous recherchez.

Cet exemple de code devrait suffire:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Je me rends compte que le downcasting est généralement une procédure dangereuse mais cette méthode ne m'a pas encore échoué.


2
Ne fonctionne pas avec Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo

1

Aujourd'hui, j'ai utilisé cette logique pourquoi je ne sais pas obtenir le nombre 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.

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

J'avais le même problème. L'utilisation ResultSet.first()de cette manière juste après l'exécution l'a résolu:

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

Documentation ( lien ):

boolean first()
    throws SQLException

Déplace le curseur sur la première ligne de cet ResultSetobjet.

Retour:

truesi le curseur se trouve sur une ligne valide; falses'il n'y a pas de lignes dans le jeu de résultats

Jette:

SQLException- si une erreur d'accès à la base de données se produit; cette méthode est appelée sur un jeu de résultats fermé ou le type de jeu de résultats estTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - si le pilote JDBC ne prend pas en charge cette méthode

Puisque:

1.2


0

Approche la plus simple, exécutez la requête Count (*), effectuez resultSet.next () pour pointer vers la première ligne, puis effectuez simplement resultSet.getString (1) pour obtenir le nombre. Code:

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

-1

Donnez un nom à la colonne ..

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

Référencez cette colonne de l'objet ResultSet dans un int et faites votre logique à partir de 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.");
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.