Як отримати підрахунок рядків за допомогою ResultSet в Java?


75

Я намагаюся створити простий метод, який отримує ResultSet як параметр і повертає int, який містить кількість рядків ResultSet. Це дійсний спосіб зробити це чи не так багато?

int size = 0;
    try {
        while(rs.next()){
            size++;
        }
    }
    catch(Exception ex) {
        System.out.println("------------------Tablerize.getRowCount-----------------");
        System.out.println("Cannot get resultSet row count: " + ex);
        System.out.println("--------------------------------------------------------");
    }

Я спробував це:

int size = 0;
try {
    resultSet.last();
    size = resultSet.getRow();
    resultSet.beforeFirst();
}
catch(Exception ex) {
    return 0;
}
return size;

Але я отримав повідомлення про помилку com.microsoft.sqlserver.jdbc.SQLServerException: The requested operation is not supported on forward only result sets.

Заздалегідь дякую за вказівники!


Ви не можете змінити оператор sql на SELECT COUNT(*) ...?
Мойше,

2
Чому ви просто не виконуєте "select count (*)" перед тим, як викликати запит, заповнюючи набір результатів? Однак ForwardOnly RS залежить від того, як ви відкриваєте з'єднання (потрібно передавати деякі параметри). Докладніше див. У документації драйвера jdbc.
BigMike

1
(OT; можливо, ви захочете використати справжню бібліотеку журналювання, яка буде дбати про такі речі, як виведення імені методу тощо)
Дейв Ньютон,

Відповіді:


67

Якщо у вас є доступ до підготовленого твердження, яке приводить до цього набору результатів, ви можете використовувати

connection.prepareStatement(sql, 
  ResultSet.TYPE_SCROLL_INSENSITIVE, 
  ResultSet.CONCUR_READ_ONLY);

Це підготує вашу заяву таким чином, що ви зможете перемотати курсор назад. Це також задокументовано в ResultSet Javadoc

Однак загалом курсори перемотування та перемотування можуть бути досить неефективними для великих наборів результатів. Іншим варіантом у SQL Server буде обчислення загальної кількості рядків безпосередньо у вашому операторі SQL:

SELECT my_table.*, count(*) over () total_rows
FROM my_table
WHERE ...

Я маю доступ до підготовленого висловлювання, але жодним чином не компетентний у SQL. Поточне підготовлене твердження виглядає так: "Виберіть m. * Із [Messages] m INNER JOIN RequestMessageIndex i на m.messageGUID = i.MessageGuid де i.requestfield = 'DR' та i.indexValue як '% TAGER00%'" Отже мені просто потрібно додати якийсь код до оператора SQL або створити інший оператор? На жаль, я справді німий, коли справа стосується SQL ...

1
@DeanGrobler: Нічого поганого в тому, щоб запитати ці речі! Обидва варіанти, про які я згадав, можуть бути дійсними. У будь-якому випадку підрахунок кількості рядків може вплинути на ефективність використання будь-якої техніки. Я особисто віддаю перевагу другому, використовуючи обчислюване count(*) over (partition by 1)поле, але лише якщо ви можете використовувати цей рахунок для таких речей, як підкачка сторінок у графічному інтерфейсі. Якщо це стосується лише ведення журналу, то я не впевнений, чи взагалі отримання підрахунку рядків є гарною ідеєю ...
Лукас Едер,

Це необхідне для отримання підрахунку рядків набору результатів; змінна повинна бути використана для побудови динамічної таблиці навколо повернених даних для графічного інтерфейсу, тому deff повинен буде це зробити. Позитивною стороною є те, що дані, що повертаються, надходять із функції пошуку. Отже, рядки набору результатів становитимуть лише близько 15 рядків.

1
@JerylCook: Існує вже існує, ResultSetі вони хочуть порахувати рядки в цьому наборі результатів. Чого мені не вистачає? "отримує ResultSet як параметр і повертає int, що містить кількість рядків ResultSet" . Моя відповідь показала їх помилку (не вказуючи ResultSet.TYPE_SCROLL_INSENSITIVE) і показала альтернативу. Отже, що саме ви критикуєте? Зверніть увагу, що є й інші відповіді, що пропонують SELECT count(*)запити ...
Лукас Едер,

1
@JerylCook: На ура :-)
Лукас Едер,

37

ваш код створення SQL-виписки може бути таким

statement = connection.createStatement();

Для вирішення "com.microsoft.sqlserver.jdbc.SQLServerException: Запитувана операція не підтримується в наборах результатів лише для пересилання", зміна вищевизначення вище

statement = connection.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE, 
    ResultSet.CONCUR_READ_ONLY);

Після зазначеної вище зміни ви можете скористатися

int size = 0;
try {
    resultSet.last();
    size = resultSet.getRow();
    resultSet.beforeFirst();
}
catch(Exception ex) {
    return 0;
}
return size;

щоб отримати підрахунок рядків


30
Statement s = cd.createStatement();
ResultSet r = s.executeQuery("SELECT COUNT(*) AS rowcount FROM FieldMaster");
r.next();
int count = r.getInt("rowcount");
r.close();
System.out.println("MyTable has " + count + " row(s).");

Іноді JDBC не підтримує такий метод, що видає помилку типу "TYPE_FORWARD_ONLY"

Sqlite не підтримує JDBC.

resultSet.last();
size = resultSet.getRow();
resultSet.beforeFirst();

Тоді використовуйте це рішення.

Дякую..


дякую, це правильне пояснення та правильне рішення.
Самбуу

8

Я щойно зробив метод геттера.

public int getNumberRows(){
    try{
       statement = connection.creatStatement();
       resultset = statement.executeQuery("your query here");
       if(resultset.last()){
          return resultset.getRow();
       } else {
           return 0; //just cus I like to always do some kinda else statement.
       }
    } catch (Exception e){
       System.out.println("Error getting row count");
       e.printStackTrace();
    }
    return 0;
}

7
отже, ви виконуєте цілий запит, лише щоб отримати підрахунок? Краще динамічно редагуйте SQL-запит, щоб він повернув лише COUNT
Джордж Бірбіліс


3

Якщо у вас є таблиця та зберігання ідентифікатора як основного та автоматичного збільшення, це спрацює

Приклад коду для отримання загальної кількості рядків http://www.java2s.com/Tutorial/Java/0340__Database/GettheNumberofRowsinaDatabaseTable.htm

Нижче наведено код

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;

public class Main {
  public static void main(String[] args) throws Exception {
    Connection conn = getConnection();
    Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
        ResultSet.CONCUR_UPDATABLE);

    st.executeUpdate("create table survey (id int,name varchar(30));");
    st.executeUpdate("insert into survey (id,name ) values (1,'nameValue')");
    st.executeUpdate("insert into survey (id,name ) values (2,null)");
    st.executeUpdate("insert into survey (id,name ) values (3,'Tom')");
    st = conn.createStatement();
    ResultSet rs = st.executeQuery("SELECT * FROM survey");

    rs = st.executeQuery("SELECT COUNT(*) FROM survey");
    // get the number of rows from the result set
    rs.next();
    int rowCount = rs.getInt(1);
    System.out.println(rowCount);

    rs.close();
    st.close();
    conn.close();

  }

  private static Connection getConnection() throws Exception {
    Class.forName("org.hsqldb.jdbcDriver");
    String url = "jdbc:hsqldb:mem:data/tutorial";

    return DriverManager.getConnection(url, "sa", "");
  }
}

2

Ваша функція поверне розмір ResultSet, але курсор буде встановлено після останнього запису, тому, не перемотуючи його, викликаючи beforeFirst (), first () або previous (), ви не зможете читати його рядки та Методи не працюватимуть лише з ResultSet вперед (ви отримаєте те саме виняток, що і у вашому другому фрагменті коду).


2

Більшість драйверів підтримують лише набір результатів вперед, тому такі методи, як last, beforeFirst тощо не підтримуються.

Перший підхід підходить, якщо ви також отримуєте дані в тому ж циклі - інакше resultSet вже повторювався і не може бути використаний знову.

У більшості випадків вимога полягає в отриманні кількості рядків, яку запит поверне, не отримуючи рядків. Перегляд набору результатів, щоб знайти кількість рядків, майже такий самий, як обробка даних. Натомість краще зробити ще один запит підрахунку (*).


У деяких системах баз даних може бути дуже повільно робити запит вибору підрахунку (*), тому, можливо, TS повинен знайти спосіб усунути необхідність знати розмір набору результатів взагалі.
Mark Rotteveel

Я майже впевнений, що драйвер JDBC SQL Server підтримує набори результатів, які можна перезаписувати? Справа в правильній ініціалізації підготовленого висловлювання ... І я погоджуюся з @MarkRotteveel, видача двох запитів лише для отримання підрахунку рядків може бути фатальним. Якщо це дійсно потрібно, ви завжди можете додати функцію count(*) over (partition by 1)вікна, навіть якщо це може бути повільним і для великих наборів результатів ...
Лукас Едер,

2

Інші вже відповіли, як вирішити вашу проблему, тому я не буду повторювати вже сказане, але скажу так: ви, мабуть, повинні знайти спосіб вирішення своїх проблем, не знаючи підсумкового набору, перед тим, як прочитати результати.

Дуже мало обставин, коли насправді потрібен підрахунок рядків перед читанням набору результатів, особливо такою мовою, як Java. Єдиний випадок, коли я вважаю необхідним підрахунок рядків, це коли підрахунок рядків є єдиними потрібними даними (у такому випадку запит підрахунку буде вищим). В іншому випадку вам краще використовувати об’єкт-обгортку для представлення даних таблиці та зберігати ці об’єкти в динамічному контейнері, такому як ArrayList. Потім, після повторення набору результатів, ви можете отримати підрахунок списку масивів. Для кожного рішення, яке вимагає знання кількості рядків перед читанням набору результатів, ви, напевно, можете подумати про рішення, яке робить це, не знаючи кількості рядків перед читанням без особливих зусиль. Думаючи про рішення, які обходять необхідність знати кількість рядків перед обробкою,

Зараз, звичайно, я не кажу, що ніколи не буває ситуацій, коли вам може знадобитися підрахунок рядків перед читанням набору результатів. Я просто кажу, що у більшості обставин, коли люди думають, що їм потрібен підрахунок результатів, перш ніж його прочитати, вони, мабуть, цього не роблять, і варто зайняти 5 хвилин, щоб подумати, чи існує інший спосіб.

Просто хотів запропонувати свої 2 центи на тему.


щоб бути коротким: якщо хтось хоче отримати і count, і елементи, найкраще, можливо, додати елементи до списку рекурсивно (за допомогою перечислювача resultSet), тоді отримайте count і скажіть списку, щоб він дав масив (якщо потрібно)
George Birbilis

1

Для мене працювали два варіанти:

1) Функція, яка повертає кількість рядків у вашому ResultSet.

private int resultSetCount(ResultSet resultSet) throws SQLException{
    try{
        int i = 0;
        while (resultSet.next()) {
            i++;
        }
        return i;
    } catch (Exception e){
       System.out.println("Error getting row count");
       e.printStackTrace();
    }
    return 0;
}

2) Створіть другий оператор SQL з опцією COUNT.


1

ResultSetМає свої методи , які переміщують курсор назад і вперед , в залежності від вибору , передбаченого. За замовчуванням це рух вперед ( TYPE_FORWARD_ONLY ResultSetтип). Якщо КОНСТАНТИ не вказують на прокручуваність та оновлення ResultSetналежним чином, ви можете отримати повідомлення про помилку. Наприклад, beforeLast() цей метод не впливає, якщо набір результатів не містить рядків. Видає помилку, якщо це не так TYPE_FORWARD_ONLY.

Найкращий спосіб перевірити, чи отримані порожні рядки --- Просто вставити новий запис після перевірки відсутності

if( rs.next() ) {

   Do nothing
} else {
  No records fetched!
}

Дивіться тут


0

Ось деякий код, який уникає отримання count для створення екземпляра масиву, але замість цього використовує ArrayList і безпосередньо перед поверненням перетворює ArrayList у необхідний тип масиву.

Зверніть увагу, що клас Supervisor тут реалізує інтерфейс ISupervisor, але в Java ви не можете передати з об'єкта [] (що повертає звичайний метод ArrayList toArray () метод ISupervisor [] (як я думаю, ви можете зробити на C #), тому ви доведеться перебирати всі елементи списку та заповнювати масив результатів.

/**
 * Get Supervisors for given program id
 * @param connection
 * @param programId
 * @return ISupervisor[]
 * @throws SQLException
 */
public static ISupervisor[] getSupervisors(Connection connection, String programId)
  throws SQLException
{
  ArrayList supervisors = new ArrayList();

  PreparedStatement statement = connection.prepareStatement(SQL.GET_SUPERVISORS);
  try {
    statement.setString(SQL.GET_SUPERVISORS_PARAM_PROGRAMID, programId);
    ResultSet resultSet = statement.executeQuery();  

    if (resultSet != null) {
      while (resultSet.next()) {
        Supervisor s = new Supervisor();
        s.setId(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ID));
        s.setFirstName(resultSet.getString(SQL.GET_SUPERVISORS_RESULT_FIRSTNAME));
        s.setLastName(resultSet.getString(SQL.GET_SUPERVISORS_RESULT_LASTNAME));
        s.setAssignmentCount(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ASSIGNMENT_COUNT));
        s.setAssignment2Count(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ASSIGNMENT2_COUNT));
        supervisors.add(s);
      }
      resultSet.close();
    }
  } finally {
    statement.close();
  }

  int count = supervisors.size();
  ISupervisor[] result = new ISupervisor[count];
  for (int i=0; i<count; i++)
    result[i] = (ISupervisor)supervisors.get(i);
  return result;
}

у новіших версіях Java ви також можете використовувати підтримку Generics у компіляторі та середовищі виконання, наприклад, ArrayList <ISupervisor>, щоб уникнути викиду результату методу toArray () (тоді він поверне ISupervisor [] замість об'єкта [], тобто магія
дженериків

Примітка. Я просто змінив "if" на "while" у зразку вище - ви можете використовувати "if", коли очікуєте лише 1 рядок у результатах
George Birbilis

-7

З http://docs.oracle.com/javase/1.4.2/docs/api/java/sql/ResultSetMetaData.html

 ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
 ResultSetMetaData rsmd = rs.getMetaData();
 int numberOfColumns = rsmd.getColumnCount();

ResultSet містить метадані, які визначають кількість рядків.


10
Рядки відрізняються від стовпців. Метадані не містять жодної інформації, що стосується рядків. Окрім отримання імен баз даних та таблиць, мова йде виключно про стовпці.
Бред Мейс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.