CftClub.ru
Клуб специалистов ЦФТ-Банк

Как правильно работать с временными таблицами.
На страницу 1, 2  След.
 
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Справочник PL/PLUS: Функции, примеры, приёмы
Предыдущая тема :: Следующая тема  
Автор Сообщение
Asdn
Участник


Вступление в Клуб: 04.10.2011
СообщениеПт Окт 14, 2011 08:19   Как правильно работать с временными таблицами. Ответить с цитатой
Полезность: Нет оценки
Добрый день!
Недавно стал сопровождать IBSO. В процессе разработки отчета потребовалось первоначально данные выбирать во временную таблицу.

из документации понял, что создать таблицу можно след образом

Код:

type typeRec is record(
                       C_CLN_TYPE integer,     -- физик/юрик
                       C_FILIAL [BRANCH],
                       C_DEPART [DEPART],
                       C_ORG_TYPE varchar2(10),
                       C_IsServiced boolean,
                       C_KIND_CREDIT varchar2(10)б
                       ........
                      );
                       
                       
type data_list is table of typeRec index by integer;


RepTable data_list;
RepTableIdx integer;


хотелось бы теперь с временной таблицей работать с помощью операций SELECT, UPDATE, DELETE, LOCATE. возможно ли такое?

например, что нибудь типа

Код:

           locate tmp in RepTable
           where
            tmp.C_ORG_TYPE = 'SMALL' and tmp.C_IsServiced = 0;

или

          select tb( tb.[Idx] : Idx )
          in RepTable all
          where
           tb.C_CLN_TYPE = 0;

или

               update tab(tab.[SUMMA] = p_nSum) in ResTable
               where tab.KIND_CREDIT = 'CRED_LINE';



пробую так делать. выдает ошибку при компиляции.


конечно можно использовать цикл FOR, перебирать последовательно записи таблицы до искомой. получать индекс этой записи. но может существует более элегантное решение?
A_A_A
Профи


Вступление в Клуб: 16.07.2009
СообщениеПт Окт 14, 2011 09:03    Ответить с цитатой
Полезность: Нет оценки
при работе с такими таблицами работать с SELECT, UPDATE .. нет возможности.

Нужно как раз использовать FOR
Asdn
Участник


Вступление в Клуб: 04.10.2011
СообщениеПт Окт 14, 2011 09:29    Ответить с цитатой
Полезность: Нет оценки
а есть ли какая нибудь альтернатива этим временным таблицам? или только is table of record?

может есть какой то другой функционал, который позволяет создавать временные таблицы для select....
svn
Профи


Вступление в Клуб: 04.02.2008
СообщениеПт Окт 14, 2011 09:37    Ответить с цитатой
Полезность: Нет оценки
есть временные структуры - но их нужно создавать в словаре
можно курсоры заюзать, представления

вы наверно пытаетесь по аналогии с чистым PL\SQL реализовать
ЦФТ это вам не это )
vtar
Эксперт


Вступление в Клуб: 20.03.2009
СообщениеПт Окт 14, 2011 09:40    Ответить с цитатой
Полезность: Нет оценки
Asdn пишет:
а есть ли какая нибудь альтернатива


1. table может быть только "приемником" результата SELECT

2. создать справочник и использовать его как временную таблицу с полноценными SELECT и UPDATE
nOnAME
Участник со стажем


Вступление в Клуб: 25.01.2011
СообщениеПт Окт 14, 2011 11:07    Ответить с цитатой
Полезность: Нет оценки
создать структруру, во вкладке "Таблица" выставить время жизни - только на время сессии(или как то так). Сгенерить там операции NEW#AUTO, EDIT#AUTO, DELETE#AUTO. И работать с ней как с таблицей. Это если хочется на ЦФТ уровне быть.
Еще можно создать временную таблицу в базе (самой базе oracle стандартными средствами). И в процессе "разработки отчета" (или операции какой-нибудь) сделать PL/SQL вставку, где делать все что угодно с Вашей таблицей на привычном языке. Тоже должно отработать.
prog
Эксперт


Вступление в Клуб: 03.03.2008
СообщениеПт Окт 14, 2011 13:20    Ответить с цитатой
Полезность: Нет оценки
Посмотрите операцию [RPT_LIC_REPORT]

там есть
Код:
select x(x.name,
            x.buy_code,
            x.statussubsys,
            (case
               when length(x.color) > 0 then '1'
               else '0'   
             end
            ),
            x.with_sql,
            x.lvl
            ) in VW_SQL_LIC_REPORT%rowtype()


в то время как VW_SQL_LIC_REPORT это

Код:
SELECT to_number(a.sort_base) sort_base,
      a.NAME NAME,
      a.buy_code buy_code,
      a.statussubsys statussubsys,
      a.color        color,
      a.with_sql     with_sql,
      a.lvl        lvl         
FROM TABLE (z$system_lib_lic_rpt.get_report (null,null)) a


фот эта функция скидывает данные в представление:
z$system_lib_lic_rpt.get_report

Вроде то что вам нужно.
Asdn
Участник


Вступление в Клуб: 04.10.2011
СообщениеПн Окт 17, 2011 02:56    Ответить с цитатой
Полезность: Нет оценки
Спасибо за ответы!
Random
Эксперт


Вступление в Клуб: 27.06.2011
СообщениеПн Окт 17, 2011 14:24   Re: Как правильно работать с временными таблицами. Ответить с цитатой
Полезность: Нет оценки
Asdn пишет:
Добрый день!
Недавно стал сопровождать IBSO. В процессе разработки отчета потребовалось первоначально данные выбирать во временную таблицу.

из документации понял, что создать таблицу можно след образом

Код:

type typeRec is record(
                       C_CLN_TYPE integer,     -- физик/юрик
                       C_FILIAL [BRANCH],
                       C_DEPART [DEPART],
                       C_ORG_TYPE varchar2(10),
                       C_IsServiced boolean,
                       C_KIND_CREDIT varchar2(10)б
                       ........
                      );
                       
                       
type data_list is table of typeRec index by integer;


RepTable data_list;
RepTableIdx integer;


Предпочитаю называть это массивами или списками, так как это ближе по смыслу.

Asdn пишет:

хотелось бы теперь с временной таблицей работать с помощью операций SELECT, UPDATE, DELETE, LOCATE. возможно ли такое?

например, что нибудь типа

Код:

           locate tmp in RepTable
           where
            tmp.C_ORG_TYPE = 'SMALL' and tmp.C_IsServiced = 0;

или

          select tb( tb.[Idx] : Idx )
          in RepTable all
          where
           tb.C_CLN_TYPE = 0;

или

               update tab(tab.[SUMMA] = p_nSum) in ResTable
               where tab.KIND_CREDIT = 'CRED_LINE';



пробую так делать. выдает ошибку при компиляции.


конечно можно использовать цикл FOR, перебирать последовательно записи таблицы до искомой. получать индекс этой записи. но может существует более элегантное решение?


Ты знаешь, можно работать и так.
По крайней мере select сделать можно.
Нужно только создать оракловский тип нужной структуры (из АРМ Администратор создаешь тип нужной структуры, потом объявляешь массив из элементов этого типа, на форме указываешь не то временный массив, не то вложенный массив) и в запросе сделать преобразование.

Вот пример:
Код:

declare
type varr$str is varray(0) of long; -- Пользоваться лучше этой конструкцией, т.к.
--   type varr$str is varray(0) of varchar2(size) вне зависимости от size подменяется на TYPE_MEMO_TABLE, а у нее длина 4000

v       varr$str;
begin
   v%init('xxx');
   debug_pipe('v='||v.count||'{'||v(1)||'}',0);

   for ( select t(t.[column_value]:cv) in v ) loop
      debug_pipe('v='||t.cv,0);
   end loop;

end;


Подробнее можно прочитать по ключевым словам:
конвеерные функции, nested-table-функция, pipeline-функция

Другое дело, что тебе вряд ли нужно именно это Smile

Другое решение - сделать индексацию не по числу, а по строке и использовать в качестве ключа заранее известное значение.
Код:
type data_list is table of typeRec index by varchar2(32767);
Asdn
Участник


Вступление в Клуб: 04.10.2011
СообщениеЧт Окт 20, 2011 02:32    Ответить с цитатой
Полезность: Нет оценки
А можно пример индексации? т.е. как задать для массива составной индекс и потом использовать его в Locate?

я поискал в документации, но у меня там приведены примеры с циклом For (по сути последовательный перебор).

т.е. например у меня какой то цикл For, в этом цикле я вычисляю ключ, по данному ключу нахожу подходящую запись в массиве и произвожу какие то вычисления для этой записи. и меняю значения ее полей.
Random
Эксперт


Вступление в Клуб: 27.06.2011
СообщениеЧт Окт 20, 2011 07:06    Ответить с цитатой
Полезность: 3
Asdn пишет:
А можно пример индексации? т.е. как задать для массива составной индекс и потом использовать его в Locate?

я поискал в документации, но у меня там приведены примеры с циклом For (по сути последовательный перебор).

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


Еще раз: есть таблица. Структура, определенная ключевыми словами create table. Существующая в базе данных вообще.
Это уровень SQL. К нему относятся операторы SQL: select, update, insert (locate, который всё равно при компиляции PL/+ -> pl/sql преобразуется к select).

Есть запись. Структура, определенная в конкретном куске pl/sql-кода словами type is record.
Есть массив записей, определенный в конкретном куске pl/sql-кода словами type is table of index by.
Это уровень PL/SQL. К нему относятся операторы pl/sql: for .. loop и НЕ относятся операторы SQL.

Конечно, в код pl/sql включаются запросы, курсоры и прочая ересь, но при этом в процессе работы происходит переключение контекста

Так вот, когда ты пишешь SQL-запрос (select/update/insert), ты переключаешься на уровень SQL и про определенные на уровне pl/sql массивы можешь забыть. Ну, не совсем забыть, но подробнее я не буду, чтоб не путать.

Для того, чтобы было удобнее пользователям, Oracle, а следом и ЦФТ, позволили пользователям осуществлять выборку из определенным образом организованных массивов с помощью оператора SQL (одного-единственного!) select from table(имя nested-массива as тип Oracle). Эта фигня не индексируется и единственное ее достоинство - что такой запрос выполняется в одном снимке/транзакции. Ну и удобство синтаксиса.
У меня большое подозрение, что всё это вырождается при компиляции в тот же цикл for, так как быстродействие аналогичное.

то есть для того, чтобы ее создать, нужно:
1) создать тип Oracle соответствующей структуры create or replace type <имя типа> as object ...
2) создать тип Oracle create type <имя типа> as table of <имя типа из шага 1>
3) в нужном месте определить переменную типа из шага 2 (nested-массив)
4) Зачитать данные в этот массив - это делается так же, как с обычным массивом, за исключением того, что обычно nested-массив нужно а) инициализировать б) расширить до нужной размерности, хотя при select into он сам расширяется
5) читать данные уже из nested-массива, указав select from table (переменная as тип) where <условия>
Пример на pl/sql:
Код:

drop type TMP#TABLE;
drop type TMP#TYPE;

create or replace type TMP#TYPE as object(
   C_EXPRESSION VARCHAR2(2000),
   C_TMP VARCHAR2(1)
);

create or replace type body TMP#TYPE as
   constructor function TMP#TYPE return self as result is
   begin return; end;
end;

alter type TMP#TYPE add constructor function TMP#TYPE return self as result cascade;

create or replace type TMP#TABLE as table of TMP#TYPE;


declare vvv TMP#TABLE;
begin
   vvv := TMP#TABLE();

   vvv.extend(100);


   for i in 1 .. 100 loop
      vvv(i) := TMP#TYPE('AAA'||i,'');
      vvv(i).c_expression := vvv(i).c_expression||'BBB';
   end loop;

   for i in (select * from table(vvv)) loop
      dbms_output.put_line(i.C_EXPRESSION);
   end loop;

   dbms_output.put_line(vvv.count);
end;


Читать данные с любыми условиями эта фигня будет по FullScan.

Для того, чтобы подобное организовать на PL/+, тебе:
1) нужно завести новый класс (справочник или структуру) нужной структуры.
2) завести новый массив элементов этого справочника, установив галочку "Вложенный массив".
3) в коде pl/+ определить тип type ляляля is varray(0) of [справочник]%rowtype; и использовать в коде pl/+ именно его.

так что я бы про Locate для массивов забыл и не путался.

пример работы с индексированным по строке обычному массиву:
Код:

declare
type rec$money is record (
   f_short varchar2(32767)
,   f_id number
,   f_name varchar2(32767)
,   f_count number
);
type map$rec is table of rec$test index by varchar2(32767);

m$list   map$rec;
begin
   for( select a(a.[CUR_SHORT]:short$
      ,   a.[CODE_ISO]:iso$
      ,   a%id:id$
      ,   a.[name]:name$
      ) in ::[FT_MONEY] all ) loop
      m$list(a.[iso$]).f_short := a.short$;
      m$list(a.[iso$]).f_id := a.id$;
      m$list(a.[iso$]).f_name := a.name$;
      m$list(a.[iso$]).f_count := 0;
   end loop;

   for (select a(a.[MAIN_V_ID]:num$) in ::[AC_FIN] all )loop
      if m$list.exists(substr(a.num$,6,3)) then
         debug_pipe('Счет '||a.[num$]||' открыт для валюты '||m$list(substr(a.num$,6,3)).f_name,0);
         m$list(substr(a.num$,6,3)).f_count := m$list(substr(a.num$,6,3)).f_count + 1;
      end if;
   end loop;
end;


На твоем месте я завёл бы временную таблицу через ARM Администратор Словаря Данных (на уровне SQL) со временем жизни в течение сессии, заполнял бы ее своими данными и работал уже с ней, как советовали выше.
Asdn
Участник


Вступление в Клуб: 04.10.2011
СообщениеПт Окт 21, 2011 04:26    Ответить с цитатой
Полезность: Нет оценки
Random, спасибо за развернутый ответ!

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

эта информация останется на будущее (

а сейчас мне приходится работать просто с массивами и выкручиватся с помощью FOR.

надеюсь, что в дальнейшем перепишу на временные таблицы.
Random
Эксперт


Вступление в Клуб: 27.06.2011
СообщениеПт Окт 21, 2011 07:29    Ответить с цитатой
Полезность: Нет оценки
Asdn пишет:
Random, спасибо за развернутый ответ!

Там рядом есть кнопочка "Оценить" Smile

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

Заводить новые операции не запрещено?
1. есть pl/sql-вставки
2. есть execute immediate
3. PL/+ позволяет читать (но не обновлять) данные из SQL-таблиц не из модели данных, не заключая их в квадратные скобки и ставя %rowtype после наименования таблицы.

и всё это можно приспособить для insert/update обычной SQL-таблицы (не из модели данных)

Код:

-- 1. Создаем таблицу
begin
   execute immediate 'CREATE global TEMPORARY TABLE test1 (
   starttestdate DATE
,   endtestdate DATE
,   results NUMBER
) ON COMMIT PRESERVE ROWS';
end;

-- 2. работаем с ней
declare
type ttt is table of test1%rowtype index by binary_integer;
v test1%rowtype;
a ttt;
begin
   select x(x%rowtype) in test1%rowtype
   into a;

   -- begin pl/sql
   forall i in 1 .. a.count
      insert into test1 values a(i);
   -- end pl/sql
end;
vek21
Участник со стажем


Вступление в Клуб: 20.09.2007
СообщениеПн Окт 24, 2011 13:31    Ответить с цитатой
Полезность: Нет оценки
Asdn, вы хотите работать с динамической таблицей с помощью select'oв PL/Plus? Это возможно.
Вот ваш пример(укороченный):

type typeRec is record(
C_CLN_TYPE integer, -- физик/юрик
C_DEPART [DEPART],
C_ORG_TYPE varchar2(10),
C_DAT DATE
);


type data_list is table of typeRec index by integer;


RepTable data_list;
RepTableIdx integer;

В "локальных описаниях" операции описываете функции:

---------------------------------------------------------
function GetReqD(i integer, cVar string) return DATE is -- возвращает ДАТУ из указ.эл-та динам.массива
begin
if cVar = 'C_DAT' then
return RepTable(i).C_DAT;
end if;
return null;
end;
---------------------------------------------------------
function GetReqN(i integer, cVar string) return number is -- возвращает ЧИСЛО из указ.атрибута указ.эл-та динам.массива
begin
if cVar = 'C_CLN_TYPE' then
return nvl(RepTable(i).C_CLN_TYPE, 0);
elsif cVar = 'C_DEPART' then
return nvl(RepTable(i).C_DEPART, 0);
end if;
return null;
end;
---------------------------------------------------------
function GetReqS(i integer, cVar string) return string is -- возвращает СТРОКУ из указ.атрибута указ.эл-та динам.массива
begin
if cVar = 'C_ORG_TYPE' then
return RepTable(i).C_ORG_TYPE;
end if;
return null;
end;
---------------------------------------------------------

Эти функции также необходимо описать в "глобальных описаниях" операции:

---------------------------------------------------------
function GetReqD(i integer, cVar string) return DATE; -- возвращает ДАТУ из указ.эл-та динам.массива
pragma restrict_references(GetReqD,wnds,wnps);
---------------------------------------------------------
function GetReqN(i integer, cVar string) return number; -- возвращает ЧИСЛО из указ.атрибута указ.эл-та динам.массива
pragma restrict_references(GetReqN,wnds,wnps);
---------------------------------------------------------
function GetReqS(i integer, cVar string) return string; -- возвращает СТРОКУ из указ.атрибута указ.эл-та динам.массива
pragma restrict_references(GetReqS,wnds,wnps);
---------------------------------------------------------

Понятно, что эти функции описывают доступ ко всем видам полей (числовым, строковым и даты) вашей динамической таблицы.

Далее, в начале "локальных описаний" операции пишете курсор

type cur is
select x
( x.[C_DAT] : C_DAT
, x.[C_CLN_TYPE] : C_CLN_TYPE
, x.[C_DEPART] : C_DEPART
, x.[C_ORG_TYPE] : C_ORG_TYPE
) in (
select y
( GetReqD(rownum, 'C_DAT') : C_DAT
, GetReqS(rownum, 'C_ORG_TYPE') : C_ORG_TYPE
, GetReqN(rownum, 'C_CLN_TYPE') : C_CLN_TYPE
, GetReqN(rownum, 'C_DEPART') : C_DEPART
) in ::[DUMMY]
where rownum <= RepTableIdx
)
where (1=1);

В этом курсоре, при необходимости, прописываете все группировки, сортировки и т.п.
А далее, в теле операции, уже можете использовать этот курсор:

for x in cursor Cur loop
....
end loop;
vek21
Участник со стажем


Вступление в Клуб: 20.09.2007
СообщениеПн Окт 24, 2011 13:39    Ответить с цитатой
Полезность: Нет оценки
.....
Извините, только сейчас увидел, как надо вставлять код программы...
..... Embarassed
Показать сообщения:   
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Справочник PL/PLUS: Функции, примеры, приёмы Часовой пояс: GMT + 3
На страницу 1, 2  След.
Страница 1 из 2

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Рейтинг@Mail.ru