Как лучше получить часть блоба?

ils
Дата: 17.09.2004 15:57:21
Здравствуйте. У меня вот какой вопрос. В базе данных в блобах хранятся текстовые файлы. Некоторые из них - больше 100 Мб. Задача состоит в том, чтобы пользователь мог визуально оценить содержимое текстового файла, т.е. чтобы часть содержимого файла вываливалась в какое-нибудь мемо-поле. (именно часть, например первые 200 строк - поскольку, понятно, что вытягивать - 100 Мб на клиента - не вариант).

У меня есть два варианта, каждый из которых не совсем подходит.

Первый вариант:
На клиенте выполнять
select dbms_lob.substr(origfile,2000,1) from origfiles where idsession=100
Функция substr возвращает тип RAW - а это максимум 2000 байт (кстати, сервер - 8.1.7). Поэтому клиенту можно вывалить не более десятка строк.

Второй вариант
Использую временную таблицу с одной строкой

create table substrblob (id number, emptyblob blob);
insert into substrblob values (1,'1');
commit;

Далее создаю процедуру

create or replace procedure PartOfBlob (v_idsession in number, v_amount in number) 
  is
  bl_dest blob;
  bl_src blob;
begin
  select emptyblob into bl_dest from substrblob where id=1 for update;
  select origfile into bl_src from origfiles where idsession=v_idsession;
  dbms_lob.copy(bl_dest,bl_src,v_amount,1,1);
  commit;
end;

На клиенте выполняется

exec PartOfBlob(100,30000);
select emptyblob from substrblob where id=1;

Здесь могут возникнуть проблемы при одновременном доступе:
сеанс А выполняет процедуру PartOfBlob
сеанс В выполняет процедуру PartOfBlob
сеанс А делает селект из substrblob

Подозреваю, что здесь существует более простое и приемлемое решение.
Может кто знает.
Зараннее спасибо
softwarer
Дата: 17.09.2004 16:15:33
Я бы создал табличную функцию (вроде как в 8.1.7 они уже есть), которая возвращает именно что первые двадцать строк. Результат получается одним селектом, реализация легко меняется.
Elic
Дата: 17.09.2004 16:30:41
А почему бы на клиенте не выполнять следующий блочок
begin
  for x in (select * from origfiles where idsession = :idsession) loop
    :MemoData := dbms_lob.substr(origfile, :amount, :offset);
    return;
  end loop;
end;
Ограничение на :MemoData - до 32k
ils
Дата: 17.09.2004 17:16:23
2 softwarer
Что есть табличная функция? Можно какую-нибудь ссылку на описание?

2 Elic
Клиентом занимаюсь не я. И к сожалению, слабо представляю, как можно с помощью него выполнить неименованный PL/SQL-блок (клиент - CBuilder + Direct Oracle Access).
Хотя как раз 32к было бы достаточно.
Elic
Дата: 17.09.2004 18:22:22
ils
как можно выполнить неименованный PL/SQL-блок (клиент - CBuilder + Direct Oracle Access).
Как конкретно это сделать в DOA не скажу, но это реализуемо и не сложно.
softwarer
Дата: 17.09.2004 18:23:46
ils
Что есть табличная функция? Можно какую-нибудь ссылку на описание?

Похоже, они все-таки появились в девятке :( Поиск по восьмой документации их не дает. А вообще - удобная штука :)

ils
Клиентом занимаюсь не я. И к сожалению, слабо представляю, как можно с помощью него выполнить неименованный PL/SQL-блок (клиент - CBuilder + Direct Oracle Access).

Да, в общем, практически так же, как и обычный оператор SQL. К примеру, PL/SQL Developer, позволяющий выполнять анонимные блоки, написан на DOA :)
ils
Дата: 17.09.2004 18:30:45
ОК, спасибо всем. Будем пробовать
CM Hungry
Дата: 17.09.2004 18:51:26
Табличная функция - это функция, возвращающая SQL-тип таблицы. В 8.1.7 точно есть, я их пользую.

[code]
CREATE OR REPLACE
TYPE "IC_REC_TYPE" is object(
unique_id integer,
name varchar2(255),
parent_id integer,
depth integer,
parentname varchar2(255),
ownerlogin varchar(32),
rec_type integer,
numchildren integer,
numpages integer)
/

CREATE OR REPLACE
TYPE "IC_TBL_TYPE" is table of ic_rec_type
/

CREATE OR REPLACE FUNCTION f_GetTree(aparent in integer, adepth in integer)
RETURN ic_tbl_type
AS
retval ic_tbl_type := ic_tbl_type();
cursor cats_cur(cparent integer, cdepth integer) is
select c1.*, level as depth from shortcatview c1
where
level <= cdepth
connect by prior c1.unique_id = c1.parent_id
start with c1.unique_id = cparent;
cats_rec shortcatview%ROWTYPE;
cursor items_cur(acat integer) is
select i.* from ItemFullInfoView i
where i.deleted = 0 and i.category_id = acat;
items_rec ItemFullInfoView%rowtype;
nc integer;
np integer;
begin
FOR cats_rec in cats_cur(aparent, adepth) LOOP
retval.extend;
select count(*) into nc from categories c where c.parent_id=cats_rec.unique_id and c.deleted = 0;
select count(*) into np from items i where i.category_id=cats_rec.unique_id and i.deleted = 0;
retval(retval.count) := ic_rec_type(cats_rec.unique_id, cats_rec.name, cats_rec.parent_id, cats_rec.depth, cats_rec.parentname, cats_rec.ownerlogin, 0, nc, np);
for items_rec in items_cur(cats_rec.unique_id) loop
retval.extend;
retval(retval.count) := ic_rec_type(items_rec.unique_id, items_rec.name, items_rec.category_id, cats_rec.depth+1, cats_rec.name, items_rec.ownername, 1, 0, 0);
end loop;
END LOOP;

RETURN retval;

end;
/
[/code]

Ну а тебе надо просто это переделать в функцию, возвращающую часть блоба построчно.
Выборка из такой функции делается так:
[code]
select * from the ( select cast( f_GetTree(:parent, :depth) as ic_tbl_type ) from dual ) a
[/code]
softwarer
Дата: 17.09.2004 19:05:13
CM Hungry
Табличная функция - это функция, возвращающая SQL-тип таблицы. В 8.1.7 точно есть, я их пользую.

Возможно, в восьмерке она назывались как-нибудь иначе, или вообще не выделялись как отдельное понятие :) Оракл любит переименовывать понятия. На tahiti.oracle.com поиск по table functions в восьмой документации не дает ничего, имеющего отношение.
CM Hungry
Дата: 17.09.2004 20:39:57
Не выделялись, видимо. Но как факт - есть.