SQL возвращает неправильное количество строк

Я пытаюсь получить 2 строки данных из моей базы данных MS Access с помощью команды SQL и с помощью средства чтения данных, но он возвращает только одну строку данных. Я точно не знаю, как работает устройство чтения данных, поэтому я думаю, что это может быть из-за того, что я неправильно закодировал его. Команда SQL должна быть в порядке, потому что я запускаю ее в запросе MS Access, и она работает. Вы знаете, что не так с моим кодом? [Редактировать: на самом деле я не пытаюсь получить количество строк, это просто для тестирования. Под фрагментом кода, который я разместил, моя программа фактически загружает данные в массив, чтобы можно было сравнить два целых числа и выбрать наименьшее из них.]

if (passageID != 1)
        {
            Connect(fileName);

            OleDbCommand com = new OleDbCommand();
            com.Connection = cxn;
            com.CommandText = "SELECT PO.OptionID_FK FROM PassageOption AS PO WHERE PO.PassageID_FK = @passageID;";
            com.Parameters.AddWithValue("@passageID", passageID);


            OleDbDataReader r = com.ExecuteReader();

            int numRows = r.RowCount;

            if (r.HasRows)
            {
                int i = 0;
                int[] optionIDs = new int[2];

                while (r.Read())
                {
                    optionIDs[i] = (int)r[i]; // It gives me the following error, the second time it runs, when i = 1; System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
                    i++;
                }

                if (optionIDs[0] < optionIDs[1])
                {
                    j = optionIDs[0];
                }
                else
                {
                    j = optionIDs[1];
                }
            }
        }

person Duane Grech    schedule 11.05.2018    source источник
comment
вы смотрите на FieldCount. Вам нужно количество строк.   -  person Wheels73    schedule 11.05.2018
comment
Кроме того: не размещение средства чтения данных в блоке using приведет к утечке пула соединений.   -  person Uwe Keim    schedule 11.05.2018
comment
... и имейте в виду, что здесь нет свойства количества строк, потому что это, вероятно, будет дорогостоящей операцией, поскольку вам потребуется повторить весь набор результатов. Вам действительно нужно получить количество строк или вы просто используете его для тестирования? (Если вам нужно получить количество, вам лучше использовать запрос SELECT COUNT......)   -  person Matt Gibson    schedule 11.05.2018
comment
Разве простой SELECT COUNT(*) FROM не будет более эффективным, чем запуск считывателя и получение счета от считывателя?   -  person Uwe Keim    schedule 11.05.2018
comment
@UweKeim: я думаю, это будет зависеть от ситуации. Если OP уверен, что будут возвращены только 2 записи, циклическое прохождение записей вряд ли будет проблемой и, вероятно, не будет медленнее, чем запрос на подсчет, за которым следует требуемый запрос. Если бы была возможность вернуть миллионы записей - другое дело.   -  person PaulF    schedule 11.05.2018
comment
@ Wheels73 У OleDB нет счетчика строк. Я только что проверил, но все же, даже если я не считаю и просто смотрю на возвращаемые данные, он возвращает одну строку, хотя, если я запускаю тот же запрос в базе данных напрямую, он возвращает 2 строки.   -  person Duane Grech    schedule 11.05.2018
comment
@DuaneGrech - правильно. oledbreader — это набор данных только для пересылки. Вы должны были прочитать до конца, чтобы получить счет. извините, моя формулировка предполагает существование свойства количества строк.   -  person Wheels73    schedule 11.05.2018
comment
@UweKeim На самом деле я использую счетчик только для целей тестирования. Мне нужны сами данные, которые я восстанавливаю в коде ниже. Должен ли я добавить его в фрагмент кода?   -  person Duane Grech    schedule 11.05.2018
comment
@MattGibson На самом деле я использую счет только для целей тестирования. Мне нужны сами данные, которые я восстанавливаю в коде ниже. Должен ли я добавить его в фрагмент кода?   -  person Duane Grech    schedule 11.05.2018
comment
@UweKeim Как поместить считыватель данных в блок использования?   -  person Duane Grech    schedule 11.05.2018
comment
да. Если ваша проблема связана с получением всех строк, нам потребуется код, который пытается получить строки.   -  person Matt Gibson    schedule 11.05.2018
comment
См. здесь информацию о том, как поместить средство чтения данных в блок использования.   -  person Uwe Keim    schedule 11.05.2018
comment
@MattGibson Я добавил код   -  person Duane Grech    schedule 11.05.2018


Ответы (3)


Средство чтения данных не знает, сколько строк будет возвращено вашим запросом на выборку, пока не прочитает все данные из базового потока, поступающего из базы данных.

Свойство FieldCount возвращает количество полей в вашем запросе, и оно ровно одно для вашего текущего запроса.

Чтобы узнать строки, вам нужно прочитать их одну за другой или использовать DataTable

int numRows = 0;
while(r.Read())
{
    // do your task with the current IDataRecord
    numRows++;
}

Console.WriteLine($"There are {numRows} rows");

Или заполнение DataTable

DataTable dt = new DataTable();
dt.Load(com.ExecuteReader());
Console.WriteLine($"There are {dt.Rows.Count} rows");

Два приведенных выше метода полезны, если вы планируете использовать возвращаемые данные (в массиве считывателя или в массиве строк таблицы), но если вы просто хотите узнать, сколько существует строк, то это лучше (хотя и минимально, когда возвращаются только две строки). ), чтобы изменить запрос на:

com.CommandText = @"SELECT COUNT(*) FROM PassageOption AS PO 
                    WHERE PO.PassageID_FK = @passageID;";
com.Parameters.AddWithValue("@passageID", passageID);
int numRows = (int)com.ExecuteScalar();

Читатель не нужен, если вам нужна одна строка с одним полем, просто ExecuteScalar

EDIT, чтобы обновить последнее редактирование

Эта строка не работает

optionIDs[i] = (int)r[i]; 

потому что у вас есть только одно поле в вашем запросе. Индексатор i следует использовать только для ссылки на массив optionIDs, чтобы не извлекать поле в позиции 1 из средства чтения. В позиции 1 нет поля, просто используйте

optionIDs[i] = (int)r[0]; 

за каждый вызов чтения

person Steve    schedule 11.05.2018
comment
Вы перебираете DataTable.Rows с помощью foreach(DataRow row in dt.Rows), а затем получаете одно поле с помощью Convert.ToInt32(row[0]). Существует много способы сделать это, но это материал для другого вопроса - person Steve; 11.05.2018
comment
Однако операция заполнения таблицы данных использует тот же OleDbDataReader. Это не влияет только на две записи и локальную базу данных, такую ​​​​как Access, но это потенциально влияет на производительность при использовании на больших наборах результатов. По сути, при заполнении, а затем зацикливании таблицы данных вы дважды зацикливаетесь на своих данных. Предпочитайте подход чтения для повышения производительности или используйте ORM для дальнейшего упрощения. - person Steve; 11.05.2018

int numRows = r.FieldCount;

Свойство FieldCount получает количество столбцов в строке, а не количество записей.

Вы выбираете только OptionID_FK одного столбца из таблицы. FieldCount покажет вам 1.

Получить нет. строк

OleDbDataReader r = com.ExecuteReader();
int rowCount = 0;
if (r.HasRows)  
{  
    while (r.Read())  
    {  
       rowCount++;     
    }  
}
r.Close(); 
person Gaurang Dave    schedule 11.05.2018
comment
Или просто SELECT COUNT(*) FROM. - person Uwe Keim; 11.05.2018
comment
Да, это возможно. Вы правы. Я попытался дать решение только в деталях его вопроса. :) - person Gaurang Dave; 11.05.2018

В вашем считывателе данных есть только один столбец, выбранный вами столбец PO.OptionID_FK. Поэтому в вашем цикле, когда i равно 1:

optionIDs[i] = (int)r[i]; // It gives me the following error, the second time it runs, when i = 1; System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

...вы пытаетесь выбрать второй столбец из модуля чтения данных (r[i]), которого не существует.

Способ работы DataReader заключается в том, что всякий раз, когда вы выполняете r.read, вы перемещаетесь на следующую строку, а ваша переменная r "массив" обновляется со всеми значениями столбца из новой строки. Индекс массива r[n] выбирает n-й столбец средства чтения, а не n-ю строку. Поэтому вы должны просто использовать:

optionIDs[i] = (int)r[0];

... который установит значение optionIDs со значением первого (нулевого) столбца DataReader для текущей строки.

person Matt Gibson    schedule 11.05.2018
comment
Привет, мне удалось исправить это самостоятельно, превратив идентификаторы параметров в список, передающий имя столбца читателю, как мне сделать это понятным в сообщении. Сорри, но я новичок на сайте. - person Duane Grech; 11.05.2018