#oracle #plsql
#Oracle #plsql
Вопрос:
У меня есть набор таблиц с разными типами данных для столбцов, и мне нужно консолидировать способ извлечения данных. Я подумал, что использование функции было бы хорошей идеей, но я не знаю, как определить одну функцию, имеющую разные типы возвращаемых данных.
Например, как определить эту функцию, чтобы иметь возможность использовать разные определения для tabletype.
CREATE OR REPLACE FUNCTION retrieve_info(field_id in integer)
RETURN pintegertypetable -- <-- how to change this to return a more generic record built dynamically in the code below?
AS
r pintegertypetable := pintegertypetable ();
BEGIN
r.extend;
r(i) := pintegertypetable (someinteger);
return r;
END;
Возможно ли это?. Есть ли лучший способ справиться с этой проблемой: разные столбцы, изначально сохраненные во множестве устаревших таблиц, и учитывая, что каждый столбец имеет разные типы данных, каким образом мы можем получить самую последнюю информацию, сохраняя исходные типы данных, без жесткого кодирования представлений, не сохраняя все в varchar2 и не приводя снова в клиентский код?
Комментарии:
1. Будет ли
SYS_REFCURSOR
подходящим для того, как вы генерируете и используете возвращаемые данные?
Ответ №1:
Вы можете реализовать это, используя слабо типизированный курсор Ref в качестве возвращаемого типа. Это особенно легко реализовать из клиентского интерфейса с использованием JDBC, поскольку возвращаемый тип курсора может быть пошаговым, как и любой результат запроса, а метаданные могут быть запрошены из ResultSet.getMetadata(). Вот пример:
CREATE OR REPLACE PROCEDURE retrieve_info(field_id in integer, p_cursor in out sys_refcursor)
AS
BEGIN
open p_cursor for 'select * from emp';
END;
Запрос в кавычках может быть любым, возвращающим любой тип для любого количества столбцов.
Ответ №2:
Используя ответ Datajam Ltd, это может быть полезно для кого-то еще:
drop table xxx1;
drop table xxx2;
drop table xxx3;
drop table xxx4;
create table xxx1(val integer);
create table xxx2(val date);
create table xxx3(val number);
create table xxx4(val varchar2(100));
insert into xxx1 (val) select rownum from all_objects where rownum <= 1;
insert into xxx2 (val) select sysdate rownum from all_objects where rownum <= 2;
insert into xxx3 (val) select 12.345 rownum from all_objects where rownum <= 3;
insert into xxx4 (val) select 'test'||rownum from all_objects where rownum <= 4;
CREATE OR REPLACE PROCEDURE retrieve_info(p_cursor in out sys_refcursor, tabname in varchar2)
AS
BEGIN
open p_cursor for 'select val from ' || tabname;
END;
/
-- CLIENT CODE IN PLSQL:
drop table logtable;
create table logtable(x varchar2(1000));
declare
v_rc sys_refcursor;
b varchar2(1000);
begin
retrieve_info(v_rc, 'xxx2'); -- here you can parameterize your call
loop
fetch v_rc into b;
exit when v_rc%notfound;
dbms_output.put_line(b);
insert into logtable(x) values(b);
end loop;
end;
/
select * from logtable;
Комментарии:
1. интересно. Итак, все преобразуется в varchar2? В основном общее приведение? Что тогда с использованием преобразований дат или числовой арифметики? Проблема не в вашем ответе, а в исходном вопросе / подходе, я полагаю.
2. Нет, здесь я использую это только для хранения и демонстрации ее работы, потому что я отключил dbms_output в консоли, где я выполнял это. Эти данные будут использоваться непосредственно в приложении с сохранением исходных метаданных.
Ответ №3:
Если вы заинтересованы в преобразовании отдельных столбцов в строки, самый простой способ — написать пакет с перегруженными функциями. Каждая функция имеет одно и то же имя, меняется только сигнатура входного параметра (ов):
SQL> create or replace package str_util
2 as
3 function s (p in varchar2) return varchar2;
4 function s (p in number) return varchar2;
5 function s (p in date, fmt in varchar2 := 'dd-mon-yyyy') return varchar2;
6 end;
7 /
Package created.
SQL>
Очевидно, что это упрощенная реализация. Для работы вам может потребоваться некоторая обработка ошибок, маски числового формата, дополнительные типы данных и т.д.
SQL> create or replace package body str_util
2 as
3 function s (p in varchar2) return varchar2
4 is
5 begin
6 return p;
7 end s;
8 function s (p in number) return varchar2
9 is
10 begin
11 return to_char(p);
12 end s;
13 function s (p in date, fmt in varchar2 := 'dd-mon-yyyy') return varchar2
14 is
15 begin
16 return to_char(p, fmt);
17 end s;
18 end;
19 /
Package body created.
SQL>
В вводящем в заблуждение запросе я показываю типы данных трех столбцов таблицы:
SQL> select dump(id) id_is_number
2 , dump(col2) col2_is_varchar
3 , dump(col3) col3_is_date
4 from big_table
5 where rownum = 1
6 /
ID_IS_NUMBER
------------------------------------------------------
COL2_IS_VARCHAR
------------------------------------------------------
COL3_IS_DATE
------------------------------------------------------
Typ=2 Len=3: 194,5,37
Typ=1 Len=11: 73,95,65,82,71,85,77,69,78,84,49
Typ=12 Len=7: 120,107,10,15,11,10,14
SQL>
В этой версии запроса я вызываю упакованную функцию и получаю обратно три строки:
SQL> select dump(str_util.s(id)) id_is_number
2 , dump(str_util.s(col2)) col2_is_varchar
3 , dump(str_util.s(col3)) col3_is_date
4 from big_table
5 where rownum = 1
6 /
ID_IS_NUMBER
------------------------------------------------------------------
COL2_IS_VARCHAR
------------------------------------------------------------------
COL3_IS_DATE
------------------------------------------------------------------
Typ=1 Len=3: 52,51,54
Typ=1 Len=11: 73,95,65,82,71,85,77,69,78,84,49
Typ=1 Len=11: 49,53,45,111,99,116,45,50,48,48,55
SQL>