Как узнать, когда прекратить заполнение OracleDataAdapter

Я использую dll OPD.NET в проекте, который обращается к оракулу.

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

Пример моей проблемы...

Если запрос на выборку возвращает 13 строк данных, приведенный ниже фрагмент кода будет выполняться без проблем до тех пор, пока не будет вызван четвертый раз oda.Fill (начальная строка равна 15, которая не существует), я полагаю, потому что он вызывает читатель, у которого есть закрытый или что-то подобное.

Затем он выдаст исключение System.InvalidOperationException с сообщением «Операция недействительна из-за текущего состояния объекта».

Как я могу узнать, сколько всего строк будет содержать команда в конечном итоге (чтобы я не столкнулся с исключением)?

OracleDataAdapter oda = new OracleDataAdapter(oracleCommand);
oda.Requery = false;

var dts = new DataTable[] { dt };
DataTable dt = new DataTable();

oda.Fill(0, 5, dts);
var a = dts[0].Rows.Count;
oda.Fill(a, 5, dts);
var b = dts[0].Rows.Count;
oda.Fill(b, 5, dts);
var c = dts[0].Rows.Count;
oda.Fill(c, 5, dts);
var d = dts[0].Rows.Count;

Примечание. Для краткости я опустил объекты команд соединения и оракула.

РЕДАКТИРОВАТЬ 1: я просто подумал, что могу просто обернуть SQL, введенный пользователем, в другой запрос и выполнить его... SELECT COUNT(*) FROM (... исходный запрос здесь...), но это не так точно чистое решение, и наверняка где-то есть метод, которого я не видел?

Заранее спасибо.


person Will    schedule 07.07.2010    source источник


Ответы (3)


Для подкачки в Oracle см.: http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html

Невозможно узнать количество наборов записей без выполнения отдельного запроса count(*). Это по дизайну. DataReader и DataAdapter предназначены только для прямого чтения и только для чтения.

Если важна эффективность (например, большие наборы записей), следует позволить базе данных выполнять разбиение по страницам и не просить OracleDataAdapter выполнить полный запрос. Представьте, если бы Google заполнил DataTable всеми более чем 1 миллионом результатов для каждого пользовательского поиска!! Следующая статья посвящена этой проблеме, хотя примеры приведены в sql:

http://www.asp.net/data-access/tutorials/efficiently-paging-through-large-amounts-of-data-cs

Я пересмотрел свой пример ниже, чтобы разрешить подкачку по любому запросу sql. Вызывающая процедура отвечает за отслеживание текущей страницы пользователя и размера страницы. Если результирующий набор меньше запрошенного размера страницы, страниц больше нет.

Конечно, запуск пользовательского sql из пользовательского ввода представляет собой огромный риск для безопасности. Но вопрос был не в этом.

Удачи! -- Бретт

DataTable GetReport(string sql, int pageIndex, int pageSize)
{
    DataTable table = new DataTable();

    int rowStart = pageIndex * pageSize + 1;
    int rowEnd = (pageIndex + 1) * pageSize;

    string qry = string.Format(
@"select * 
from (select rownum ""ROWNUM"", a.*
    from ({0}) a
    where rownum <= :rowEnd)
where ""ROWNUM"" >= :rowStart
", sql);
    try
    {
        using (OracleConnection conn = new OracleConnection(_connStr))
        {
            OracleCommand cmd = new OracleCommand(qry, conn);
            cmd.Parameters.Add(":rowEnd", OracleDbType.Int32).Value = rowEnd;
            cmd.Parameters.Add(":rowStart", OracleDbType.Int32).Value = rowStart;
            cmd.CommandType = CommandType.Text;
            conn.Open();
            OracleDataAdapter oda = new OracleDataAdapter(cmd);
            oda.Fill(table);
        }
    }
    catch (Exception)
    {
        throw;
    }
    return table;        
}
person Brett    schedule 07.07.2010
comment
Привет, Бретт, это действительно дает мне другой способ заполнения подмножества набора результатов, когда rowStart заменяет первый параметр, а rowEnd заменяет второй параметр (сколько записей нужно получить). Мне все еще нужно сделать отдельный запрос, чтобы найти максимальное значение rowStart. Спасибо, тоже интересная ссылка. - person Will; 08.07.2010
comment
Похоже, вы пытаетесь реализовать решение для подкачки, используя DataTable в качестве контейнера. Мне непонятно, почему это решение для подкачки не работает. Предположим, вы хотите просмотреть 10 записей за раз. Тогда rowStart и rowEnd будут чем-то вроде (1,20), (21,30), (31,40) и т. д. Когда таблица данных возвращается пустой или содержит менее 10 строк, все готово. - person Brett; 08.07.2010
comment
Преимущество использования метода заполнения OracleDataAdapter заключается в том, что он фактически не повторно запрашивает базу данных (с requery = false) — он просто заполняет дополнительные данные. Проблема с использованием логики Когда таблица данных возвращается пустой или с менее чем 10 строками, вы закончили тем, что последняя возвращенная таблица данных может фактически содержать последнюю строку, и в этом случае исключение все равно будет выдано. - person Will; 09.07.2010
comment
Смотрите мой пересмотренный ответ. Удачи. - person Brett; 09.07.2010

Вы можете добавить аналитический COUNT к вашему запросу:

SELECT foo, bar, COUNT(*) OVER () TheCount WHERE ...;

Таким образом, счетчик всего запроса возвращается с каждой строкой в ​​TheCount, и вы можете настроить цикл так, чтобы он завершался соответствующим образом.

person DCookie    schedule 07.07.2010

Чтобы получить контроль над Fill DataTable Loop, вам нужно владеть циклом.

Затем создайте свою собственную функцию для заполнения таблицы данных с помощью OracleDataReader.

Чтобы получить информацию о столбцах, вы можете использовать dataReader.GetSchemaTable.

Чтобы заполнить таблицу:

  MyTable.BeginLoadData 
  Dim Values(mySchema.rows.count-1)
  Do while myReader.read
    MyReader.GetValues(Values)
    MyTable.Rows.add(Values)

    'Include here your control over load Count 
  Loop
  MyTable.EndLoadData
person x77    schedule 07.07.2010