S3rval03 Ответов: 2

Как получить длинное необработанное поле без буферизации всех данных


Всем привет,

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

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

Есть ли способ загрузить все двоичное содержимое моего поля (dj.doc) в память в моем объекте (dto) без использования InitialLONGFetchSize = -1 ?

Спасибо за Ваши советы.

Я работаю на сервере баз данных Oracle 10g с клиентом Oracle v12 (32 бита) и Visual Studio Pro 2017 (15.4.5) с языком C#

public static void Extract2Files(string scodeUF)
        {
            IDbCommand command = GetStoredProcCommand("GetAllOnlyForUF");
            command.Parameters.Add(CreateParameter("pin_id_unite_m", scodeUF, 12));
            command.Parameters.Add(CreateOutputCursorParameter("t_cursor"));
            
            command.InitialLONGFetchSize = -1; 

            command.Connection.Open();

            OracleDataReader reader = (OracleDataReader)command.ExecuteReader(); 
            // if (reader.HasRows) // <== this command is buffering all datas from the cursor sent by th stored procédure. I don't want this behavior !
                //{
                    
                    DtoParser parser = DtoParserFactory.GetParser(typeof(T));
                    parser.PopulateOrdinals(reader);

                    while (reader.Read()) // <== this command is buffering all datas from the cursor sent by th stored procédure. I don't want this behavior !
                    {
                        T dto = default(T); // null;
                        dto = (T)parser.PopulateDto(reader);
                        ...
                    }
                    reader.Close();
                //}


В функции PopulateDto, я вызываю функцию, называемую GetBinaryFromReaderInOnePass.
Приведенный ниже код работает только при выполнении команды.InitialLONGFetchSize = -1
Если команда.InitialLONGFetchSize = 0 значение retval всегда равно 0.

static public byte[] GetBinaryFromReaderInOnePass(IDataReader rdr, int ordinalBinaire)

// Size of the BLOB buffer.
int totalBinaryBufferSize = (int)rdr.GetBytes(ordinalBinaire, startIndex, null, 0, 0);

// The BLOB byte[] buffer to be filled by GetBytes
byte[] outByte = new byte[totalBinaryBufferSize];

// Read bytes into outByte[] and retain the number of bytes returned.  
retval = rdr.GetBytes(ordinalBinaire, startIndex, outByte, 0, totalBinaryBufferSize);


Вот сохраненный процесс, где dj.doc это длинное поле необработанного типа :

PROCEDURE GetAllOnlyForUF(pin_id_unite_m VARCHAR2, cur_docsJoints out t_cursor) IS
BEGIN
    OPEN cur_docsJoints FOR SELECT dj.IDT, dj.name, dj.doc
                            FROM TB_PAT_DOC_JOINT dj, TB_MVT m
                            WHERE dj.id_mvt = m.id_mvt 
                            AND m.id_unite_m = pin_id_unite_m;
END GetAllOnlyForUF;


Что я уже пробовал:

Когда я использую команду.InitialLONGFetchSize = -1, оперативная память заполняется всеми двоичными файлами моего курсора. Это нецелесообразно с тысячами записей, где каждый двоичный файл весит 2 Мб

Когда я использую команду.InitialLONGFetchSize = 0, я не могу определить размер длинного необработанного поля (DOC) с помощью метода GetBytes

Когда я использую команду.InitialLONGFetchSize = 4096, все мои двоичные файлы имеют размер 4 КБ.
для восстановления всего двоичного файла не было предпринято никаких операций с базой данных туда и обратно.

Я не могу найти выхода ...

2 Ответов

Рейтинг:
5

S3rval03

Подводя итог для всех читателей у меня было две проблемы:

1) Во-первых, количество записей, полученных по умолчанию при InitialLONGFetchSize = -1.

Этот первый пункт может быть установлен так, как предложил Йорген с читателем.FetchSize = читатель.Размер Строки * N,
например, при N = 1 обрабатывать записи одну за другой.

2) второй проблемой было значение размера длинных необработанных полей, возвращаемое функцией GetBytes при InitialLONGFetchSize <> -1.
Это происходит системно, когда я вызываю свою хранимую процедуру из кода C # в Visual Studio.
Когда я заменяю вызов процедуры запросом, определенным в моем коде C # в виде строки,
У меня все еще есть проблема с моим запросом, если он имеет соединение между 2 таблицами.

string requeteDocJointUf = "SELECT dj.IDT, dj.NOM_DOC, dj.doc " +
                            " FROM TB_PAT_DOC_JOINT dj, TB_MVT m " +
                            " WHERE dj.id_mvt = m.id_mvt " +
                            " AND m.id_unite_m = '" + scodeUF + "'";


Действительно, глядя на читателя.GetSchemaTable (), я вижу, что мой первичный ключ (dj.IDT) не распознается как ключ

Property: ColumnName                     Value: IDT 
Property: IsUnique                       Value: False
Property: IsKey                          Value: False
Property: IsRowID                        Value: False	


С другой стороны, если я удаляю соединение, то мой ключ распознается, и функция GetBytes возвращает размер моего двоичного поля.

string requeteDocJointUf = "SELECT dj.IDT, dj.NOM_DOC, dj.doc " +
                            " FROM TB_PAT_DOC_JOINT dj " +
                            " WHERE dj.IDT= 274934";

Property: ColumnName                     Value: IDT 
Property: IsUnique                       Value: True
Property: IsKey                          Value: True <==
Property: IsRowID                        Value: False				


InitialLONGFetchSize = 0;

// Size of the BLOB buffer.
int totalBinaryBufferSize = (int)rdr.GetBytes(ordinalBinaire, startIndex, null, 0, 0); 
// <== totalBinaryBufferSize recovers well 136671 while InitialLONGFetchSize = 0! Yes!
// Property: IsKey  Value = True  for IDT 

// The BLOB byte[] buffer to be filled by GetBytes
byte[] outByte = new byte[totalBinaryBufferSize];

// Read bytes into outByte[] and retain the number of bytes returned.  
retval = rdr.GetBytes(ordinalBinaire, startIndex, outByte, 0, totalBinaryBufferSize); 
// <== the binary file is correctly recovered !!


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

Когда я удаляю соединение в курсоре процедуры, мой ключ все еще не распознается:

TYPE t_cursor is ref cursor RETURN TB_PAT_DOC_JOINT%ROWTYPE;

PROCEDURE GetAllOnlyForUF(pin_id_unite_m VARCHAR2, cur_docsJoints out t_cursor) IS
BEGIN
    OPEN cur_docsJoints FOR SELECT * -- dj.IDT, dj.NOM_DOC, dj.doc
                            FROM TB_PAT_DOC_JOINT dj
                            WHERE dj.IDT = 274934;
END GetAllOnlyForUF;


Jörgen Andersson

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

Рейтинг:
19

Jörgen Andersson

Используйте InitialLONGFetchSize = 0
Причина, по которой вы не получаете returnvalue, вероятно, заключается в том, что читатель не знает, как определить правильное returnvalue.
Вам нужно включить либо первичный ключ, либо идентификатор строки в результирующий набор. Получение данных от OracleDataReader[^]

Если вы хотите настроить объем данных буферов считывателя, вы можете, например, установить reader.FetchSize = reader.RowSize * 100; если вы хотите буферизировать 100 строк.


S3rval03

Прежде всего спасибо Вам, Йорген, за быстроту вашего ответа.
На самом деле я не знал читателя.Свойство FetchSize, которое позволяет установить желаемое значение вместо значения по умолчанию 131072.
Это позволяет установить InitialLONGFetchSize = -1 и ограничить время ожидания выборки для каждого оператора Read, установив небольшое число: reader.FetchSize = читатель.Размер строки * 1;
В этом случае моя функция GetBinaryFromReaderInOnePass успешно извлекла двоичный файл.
Это отчасти отвечает моей потребности, большое вам спасибо.
Однако мне кажется, что путем установки читателя.FetchSize = читатель.RowSize * 1, обработка менее оптимизирована во времени обработки.
Разве нет решения в геттере (с GetBytes)?
У меня уже был свой первичный ключ (диджей.Поле ID) в предложении Select, но если есть сомнения, я также добавил диджей.Идентификатор rowid.
Установив InitialLONGFetchSize в:
- 0 ==> восстановленные двоичные файлы имеют размер 0
- 4096 ==> восстановленные двоичные файлы имеют размер 4096
В обоих случаях двоичные файлы являются неполными
Это должно быть связано с тем, как я использую функцию GetBytes, которая предполагает, что она будет читать все сразу.
Я уже читал онлайн-справку Oracle и понял, что обратный переход к базе данных для чтения остальной части двоичного поля обрабатывается автоматически.
Так ли это на самом деле?
Как это может работать с функцией GetBytes, которая запрашивает размер буфера для заполнения?
Если я вызываю несколько раз GetBytes, как я узнаю, что конец двоичного файла был достигнут, не зная реального конечного размера двоичного файла?
Если бы я узнал, как постепенно заполнить еще один байт [] в несколько раз, в то время как его размер должен быть объявлен заранее?
Это много вопросов, которые я знаю. Большое вам спасибо, если у вас есть еще какие-то предложения.

Jörgen Andersson

Я немного углубился в то, что здесь происходит.
Похоже, что использование хранимой процедуры и курсора запрещает OracleDatareader извлекать ключевую информацию. И в этом случае весь документ должен быть извлечен непосредственно. (InitialLONGFetchSize = -1)

Чтобы узнать, так ли это, вы можете открыть schematable и проверить информацию о поле DataTable SchemaTable = reader.Getschematable в();

Другой тест, который вы можете сделать, - это получить информацию непосредственно, удалив выходной параметр, установив CommandType = System.Data.Командный тип.Текст и команда text = "SELECT dj.IDT, dj.name, dj.doc
От TB_PAT_DOC_JOINT dj, TB_MVT m
Где dj.id_mvt = m.id_mvt
И m.id_unite_m = :pin_id_unite_m;

S3rval03

Большое вам спасибо, Йорген, за ту неоценимую помощь, которую вы мне оказали !!

Я поместил детали своих выводов в поле "мое решение".

Jörgen Andersson

Пожалуйста.