Мы просто преобразовали наши хранимые процедуры SQL-сервера в процедуры оракула. SQL-сервер SP очень зависел от таблиц сессии (INSERT INTO #table1...
) эти таблицы были преобразованы как глобальные временные таблицы в оракуле. Мы закончили с aroun 500 GTT's для наших 400 SP
Теперь мы узнаем, что работа с GTT's в оракуле рассмотрена последняя возможность из-за производительности и других проблем.
что другие альтернативы там? Наборы? Курсоры?
Наше типичное использование GTT's похоже так:
Вставьте в GTT
INSERT INTO some_gtt_1
(column_a,
column_b,
column_c)
(SELECT someA,
someB,
someC
FROM TABLE_A
WHERE condition_1 = 'YN756'
AND type_cd = 'P'
AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
AND (lname LIKE (v_LnameUpper || '%') OR
lname LIKE (v_searchLnameLower || '%'))
AND (e_flag = 'Y' OR
it_flag = 'Y' OR
fit_flag = 'Y'));
Обновите GTT
UPDATE some_gtt_1 a
SET column_a = (SELECT b.data_a FROM some_table_b b
WHERE a.column_b = b.data_b AND a.column_c = 'C')
WHERE column_a IS NULL OR column_a = ' ';
и позже вытащите данные из GTT. Это просто демонстрационные запросы, в действительности запросы действительно complext с партией соединений и подзапросов.
У меня есть три вопроса о части:
"Почему нужно уходить от GTT? действительно так плохи."
Пару дней назад я делал пробный вариант, который загружал большой XML файл (~18MB) в XMLType. Поскольку я не хотел хранить XMLType постоянно, я попробовал загрузить его в переменную PL/SQL (память сессии) и во временную таблицу. Загрузка во временную таблицу заняла в пять раз больше времени, чем загрузка в переменную XMLType (5 секунд по сравнению с 1 секундой). Разница заключается в том, что временные таблицы не являются структурами памяти: они записываются на диск (в частности, в назначенное вами временное табличное пространство).
Если вы хотите кэшировать много данных, то хранение их в памяти будет нагружать PGA, что не очень хорошо, если у вас много сессий. Таким образом, это компромисс между оперативной памятью и временем.
"Может ли кто-нибудь показать, как преобразовать приведенные выше примеры запросов в коллекции и/или курсоры?"
Запросы, которые вы разместили, можно объединить в один оператор:
SELECT case when a.column_a IS NULL OR a.column_a = ' '
then b.data_a
else column_a end AS someA,
a.someB,
a.someC
FROM TABLE_A a
left outer join TABLE_B b
on ( a.column_b = b.data_b AND a.column_c = 'C' )
WHERE condition_1 = 'YN756'
AND type_cd = 'P'
AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
AND (lname LIKE (v_LnameUpper || '%') OR
lname LIKE (v_searchLnameLower || '%'))
AND (e_flag = 'Y' OR
it_flag = 'Y' OR
fit_flag = 'Y'));
(я просто перенес вашу логику, но этот оператор case()
можно заменить на более аккуратный nvl2(trim(a.column_a), a.column_a, b.data_a)
).
Я знаю, вы говорите, что ваши запросы стали сложнее, но первым делом вам следует подумать о том, чтобы переписать их. Я знаю, как соблазнительно разбить сложный запрос на множество детских SQL, сшитых вместе с помощью PL/SQL, но чистый SQL намного эффективнее.
Для использования коллекции лучше всего определить типы в SQL, потому что это дает нам возможность гибко использовать их как в SQL-запросах, так и в PL/SQL.
create or replace type tab_a_row as object
(col_a number
, col_b varchar2(23)
, col_c date);
/
create or replace type tab_a_nt as table of tab_a_row;
/
Вот пример функции, которая возвращает набор результатов:
create or replace function get_table_a
(p_arg in number)
return sys_refcursor
is
tab_a_recs tab_a_nt;
rv sys_refcursor;
begin
select tab_a_row(col_a, col_b, col_c)
bulk collect into tab_a_recs
from table_a
where col_a = p_arg;
for i in tab_a_recs.first()..tab_a_recs.last()
loop
if tab_a_recs(i).col_b is null
then
tab_a_recs(i).col_b := 'something';
end if;
end loop;
open rv for select * from table(tab_a_recs);
return rv;
end;
/
И вот она в действии:
SQL> select * from table_a
2 /
COL_A COL_B COL_C
---------- ----------------------- ---------
1 whatever 13-JUN-10
1 12-JUN-10
SQL> var rc refcursor
SQL> exec :rc := get_table_a(1)
PL/SQL procedure successfully completed.
SQL> print rc
COL_A COL_B COL_C
---------- ----------------------- ---------
1 whatever 13-JUN-10
1 something 12-JUN-10
SQL>
В функции необходимо инстанцировать тип с колонками, чтобы избежать исключения ORA-00947. Это не требуется при заполнении типа таблицы PL/SQL:
SQL> create or replace procedure pop_table_a
2 (p_arg in number)
3 is
4 type table_a_nt is table of table_a%rowtype;
5 tab_a_recs table_a_nt;
6 begin
7 select *
8 bulk collect into tab_a_recs
9 from table_a
10 where col_a = p_arg;
11 end;
12 /
Procedure created.
SQL>
"Какими должны быть рекомендации по тому, когда использовать и когда избегать GTT"
Глобальные временные таблицы очень хороши, когда нам нужно поделиться кэшированными данными между различными программными блоками в одном сеансе. Например, если у нас есть общая структура отчета, создаваемая одной функцией, питающейся от GTT, которая заполняется одной из нескольких процедур. (Хотя даже это можно реализовать с помощью динамических курсоров ссылок ...)
Глобальные временные таблицы также хороши, если у нас много промежуточной обработки, которая слишком сложна, чтобы ее можно было решить одним SQL-запросом. Особенно если эта обработка должна быть применена к подмножествам извлекаемых строк.
Но в целом следует исходить из того, что нам не нужно использовать временную таблицу. Поэтому
Обычно я бы использовал коллекцию PL / SQL для хранения небольших объемов данных (может быть, тысячи строк). Если бы объемы данных были намного больше, я бы использовал GTT, чтобы они не перегружали память процесса.
Итак, я мог бы выбрать несколько сотен строк из базы данных в коллекцию PL / SQL, затем просмотреть их в цикле, чтобы выполнить некоторые вычисления / удалить несколько или что-то еще, а затем вставить эту коллекцию в другую таблицу.
Если бы я имел дело с сотнями тысяч строк, я бы попытался вложить как можно больше «тяжелой работы» в большие операторы SQL. Это может потребовать, а может и не потребовать GTT.
Вы можете использовать объекты коллекции уровня SQL как нечто, что довольно легко переводится между SQL и PL / SQL
create type typ_car is object (make varchar2(10), model varchar2(20), year number(4));
/
create type typ_coll_car is table of typ_car;
/
select * from table (typ_coll_car(typ_car('a','b',1999), typ_car('A','Z',2000)));
MAKE MODEL YEAR
---------- -------------------- ---------------
a b 1,999.00
A Z 2,000.00
declare
v_car1 typ_car := typ_car('a','b',1999);
v_car2 typ_car := typ_car('A','Z',2000);
t_car typ_coll_car := typ_coll_car();
begin
t_car := typ_coll_car(v_car1, v_car2);
FOR i in (SELECT * from table(t_car)) LOOP
dbms_output.put_line(i.year);
END LOOP;
end;
/