plpgsql stored function that creates table with parameter column names - stored-procedures

I want to create a Postgres Stored function in plpgsql that creates a table with specified column names that vary according to input parameters.
Basically something like this:
CREATE OR REPLACE FUNCTION document_insert_new_document(_name text, _table_name text)
RETURNS bigint AS
$BODY$
declare
_documentid bigint;
_user_history_table_name text;
_history_table_name_column text;
begin
_documentid = 0;
_user_history_table_name = 'merge_user_history_' || _table_name;
_history_table_name_column = _table_name || '_id';
CREATE TABLE _user_history_table_name
(
user_history_id bigint NOT NULL,
_history_table_name_column bigint NOT NULL,
...
)
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Is this possible? And if so, how?

CREATE OR REPLACE FUNCTION document_insert_new_document(_name text, _table_name text)
RETURNS void AS
$BODY$
declare
_documentid bigint;
_user_history_table_name text;
_history_table_name_column text;
query_create text;
begin
_documentid = 0;
_user_history_table_name = 'merge_user_history_' || _table_name;
_history_table_name_column = _table_name || '_id';
query_create := 'CREATE TABLE ' || _user_history_table_name ||
' (user_history_id bigint NOT NULL,' ||
_history_table_name_column ||
' bigint NOT NULL,
... )
WITH (
OIDS=FALSE
)';
EXECUTE query_create;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Or, if you wanna use 'USING' clause:
[...]
query_create := 'CREATE TABLE ($1) (
user_history_id bigint NOT NULL,
($2) bigint NOT NULL,
... )
WITH (
OIDS=FALSE
)';
EXECUTE query_create USING _user_history_table_name, _history_table_name_column;
[...]

Related

Drop and Create table if exist if not then Create in oracle Procedure

I have a query that takes start and end year for data also it takes a name of a table as procedure parameters..
the query then will create a table with that name if it doesn't exist or if exist it will drop and recreate it
CREATE OR REPLACE Procedure USE_RAWDATA
( START_RP IN NUMBER, END_RP IN NUMBER,TABLE_NAME varchar)
IS
v_listStr CLOB;
DAT VARCHAR(500):=to_char(to_date(SYSDATE), 'yyyymmdd');
cnt NUMBER;
BEGIN
BEGIN
SELECT COUNT(*) INTO cnt FROM user_tables WHERE table_name = TABLE_NAME||'_'||DAT;
dbms_output.put_line(TABLE_NAME||'_'||DAT);
dbms_output.put_line(cnt);
IF (cnt) = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE '||TABLE_NAME||'_'||DAT;
dbms_output.put_line('DROP TABLE '||TABLE_NAME||'_'||DAT);
END IF;
END;
select rtrim(xmlagg(xmlelement(e,column_name,', ').extract('//text()') order by column_id).getclobval(),', ') x INTO v_listStr from RAW_DATA_METADATA WHERE (START_ROUND<= END_RP and END_ROUND is NULL) or (START_ROUND >= START_RP and END_ROUND <= END_RP );
EXECUTE IMMEDIATE 'CREATE TABLE '||TABLE_NAME||'_'||DAT||' AS SELECT '||v_listStr ||' FROM RAW_DATA WHERE ROUND_ID BETWEEN '||START_RP ||' AND '||END_RP;
END;
/
my problem is that cnt is always giving me 0 even the table exists then the procedure will end with an error table already exist...
I don't know why .. since when I try it as PL\SQL query it gives me a correct result cnt =1 if exist
declare
v_listStr CLOB;
DAT VARCHAR(500):=to_char(to_date(SYSDATE), 'yyyymmdd');
cnt NUMBER;
BEGIN
BEGIN
SELECT COUNT(*) INTO cnt FROM user_tables WHERE :TABLE_NAME = :TABLE_NAME||'_'||DAT;
dbms_output.put_line(:TABLE_NAME||'_'||DAT);
dbms_output.put_line(cnt);
IF (cnt) = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE '||:TABLE_NAME||'_'||DAT;
dbms_output.put_line('DROP TABLE '||:TABLE_NAME||'_'||DAT);
END IF;
END;
select rtrim(xmlagg(xmlelement(e,column_name,', ').extract('//text()') order by column_id).getclobval(),', ') x INTO v_listStr from RAW_DATA_METADATA WHERE (START_ROUND<= :END_RP and END_ROUND is NULL) or (START_ROUND >= :START_RP and END_ROUND <= :END_RP );
EXECUTE IMMEDIATE 'CREATE TABLE '||:TABLE_NAME||'_'||DAT||' AS SELECT '||v_listStr ||' FROM RAW_DATA WHERE ROUND_ID BETWEEN '||:START_RP ||' AND '||:END_RP;
END;
/
can you help me figure out why it always cnt gives 0 even the table is exist
This query can only ever return 0:
select count(*) into cnt
from user_tables
where table_name = table_name || '_' || dat;
You need to either prefix table_name with the procedure name
select count(*) into cnt
from user_tables
where table_name = use_rawdata.table_name || '_' || dat;
or else name your parameters differently (tableName, p_table_name etc).
I'd also suggest removing the first begin and end as they aren't doing anything, and adjusting your indentation to reflect the code structure more accurately.
I make it something like this:
create or replace procedure use_rawdata
( start_rp in number
, end_rp in number
, table_name varchar2 )
is
v_liststr clob;
dat varchar2(8) := to_char(sysdate, 'yyyymmdd');
cnt number;
begin
select count(*) into cnt
from user_tables
where table_name = upper(use_rawdata.table_name) || '_' || dat;
dbms_output.put_line(table_name || '_' || dat);
dbms_output.put_line(cnt);
if cnt = 1 then
execute immediate 'DROP TABLE ' || table_name || '_' || dat;
dbms_output.put_line('DROP TABLE ' || table_name || '_' || dat);
end if;
select rtrim(xmlagg(xmlelement(e, column_name, ', ').extract('//text()') order by column_id).getclobval(), ', ') x
into v_liststr
from raw_data_metadata
where (start_round <= end_rp and end_round is null)
or (start_round >= start_rp and end_round <= end_rp);
execute immediate 'create table ' || table_name || '_' || dat || ' as select ' || v_liststr || ' from raw_data where round_id between ' || start_rp || ' and ' || end_rp;
end;

How to execute a multi row cursor as a table report

Dear friends i have done this:
FUNCTION ALL_CLIENTS_DATA
( client_in IN varchar2)
RETURN sys_refcursor
IS c1 sys_refcursor;
BEGIN
OPEN c1 FOR
Select TO_CHAR('SELECT '''||ATR_NOMBRE_ATRIBUTO||''','||ATR_VALOR_ATRIBUTO||' FROM '||ATR_VALOR_TABLA||' WHERE NRO_CLIENTE='''||client_in||''';')FROM GNT_ATRIBUTO WHERE ATR_TAB_IDENTIFICADOR = 7;
RETURN c1;
END;
And receiving this as cursor/function value:
SELECT 'Estado',TO_CHAR(DECODE(estado,0,'ACTIVO',1,'ELIMINADO',2,'RETIRADO',3,'NUEVO',4,'PROCESO DE RETIRO','No Disponible')) FROM cliente WHERE NRO_CLIENTE='253417';
SELECT 'SED-Cuadro-Llave',cadena FROM cliente WHERE NRO_CLIENTE='253417';
SELECT 'Sector',to_char(sector) FROM cliente WHERE NRO_CLIENTE='253417';
SELECT 'Zona',to_char(zona) FROM cliente WHERE NRO_CLIENTE='253417';
.
.
.
And this if I execute line for line (alone for testing):
Estado ACTIVO
SED-Cuadro-Llave 00100S / 21 / 3SP
Sector 89
Zona 291
.
.
.
I just don't know how to display my final result in a single execution. Please help.
If you want all the attributes in one row you need to assemble a query from the result set of GNT_ATRIBUTO then open the cursor for that.
FUNCTION ALL_CLIENTS_DATA
( client_in IN varchar2)
RETURN sys_refcursor
IS
stmt varchar2(32767);
c1 sys_refcursor;
l_tgt_table varchar2 (32767);
BEGIN
stmt := 'select ';
for lrec in (
Select ATR_NOMBRE_ATRIBUTO
, ATR_VALOR_ATRIBUTO
, ATR_VALOR_TABLA
, rownum as rn
from GNT_ATRIBUTO
where ATR_TAB_IDENTIFICADOR = 7
)
loop
if lrec.rn > 1 then
stmt := stmt || ',';
end if;
stmt := stmt || lrec.ATR_VALOR_ATRIBUTO
|| ' as "' || lrec.ATR_NOMBRE_ATRIBUTO || '"';
l_tgt_table := lrec.ATR_VALOR_TABLA;
end loop;
stmt := stmt || ' from ' || l_tgt_table
|| ' WHERE NRO_CLIENTE= :p1 ';
OPEN c1 FOR stmt using client_in;
RETURN c1;
END;

IBM DB2 Equivalent syntax for SQL Server Stored Procedure - error while deploying

I am newbie to IBM db2.Need to convert the below mentioned SP to db2 syntax. But i am stuck with many equivalents used or available in Db2. Even google research doesn't show how exactly we can compare object id of tables in db2 as I am doing in SQL Server stored procedure. Could anyone suggest me with right way to proceed?
EDIT: I have updated with equivalent DB2 syntax, but facing below error while deploying at the particular line, Can anyone identify and help me understand what is wrong with this syntax or the problem lies anywhere else in the procedure.
line no 25 : DECLARE v_sqlstate CHAR(5);
BACKUPTABLE: 25: An unexpected token "<variable declaration> was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.18.60
An unexpected token variable declaration was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.18.60
SQL Server Stored procedure syntax:
CREATE PROCEDURE [dbo].[BackUpTable]
#TableName sysname
AS
BEGIN
SET nocount ON
DECLARE #sql VARCHAR(500)
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = Object_id(N'[dbo].[' + #TableName+'_EST' + ']')
AND TYPE IN ( N'U' ))
BEGIN
SET #sql = 'declare #Done bit
set #Done = 0
while #Done = 0
begin
delete top (100000)
from ' + #TableName + '_Bak' +
' if ##rowcount = 0
set #Done = 1
end;'
SET #sql = #sql + 'insert into ' + #TableName + '_Bak select * from ' +
#TableName +'_EST'
EXEC(#sql)
END
ELSE
BEGIN
DECLARE #err_message VARCHAR(300)
SELECT #err_message = 'The table "' + Isnull(#TableName, 'null') +
'" does not exist'
RAISERROR (#err_message, 16, 1)
END
END
DB2 SYNTAX CREATED SO FAR:
CREATE OR REPLACE PROCEDURE BackUpTable (IN TableName VARCHAR(128))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE dynamicSql VARCHAR(500);
IF(EXISTS(
SELECT * FROM SYSIBM.SYSTABLES
WHERE NAME = TableName||'_EST'
)
)
THEN
SET dynamicSql = 'DELETE FROM '||TableName ||'_BAK';
SET dynamicSql = dynamicSql ||'insert into ' || TableName || '_BAK select * from ' ||
TableName || '_EST';
EXECUTE IMMEDIATE dynamicSql;
ELSE
DECLARE v_sqlstate CHAR(5);
DECLARE v_sqlcode INT;
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
DECLARE SQLCODE INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT SQLSTATE, SQLCODE
INTO v_sqlstate, v_sqlcode
FROM sysibm.sysdummy1;
SET O_Error_Msg = 'TABLE IS NOT AVAILABLE:: SQLState : '||v_sqlstate||' SQLCode : '||v_sqlcode ;
END;
END IF;
END
on z/os you can do it:
IF( EXISTS( SELECT 1 FROM QSYS2.SYSTABLES WHERE TABLE_SCHEMA = 'YOURLIB' AND TABLE_NAME = 'YOURTABLENAME')) THEN
DROP TABLE YOURLIB.YOURTABLENAME;
END IF;

Intermittent errors when performing export to file in Oracle 11g

System info
Oracle database 11g/11.2 Enterprise edition
Summary
When the procedure is called (via Hibernate's CallableStatements), it gets all the partitions to be exported based on the days to keep (an input parameter). After those partitions are evaluated, the function which accepts partitions lists as input parameters (the first one in the wall of code) uses the DBMS_DATAPUMP API to export the content into a flat file.
Sometimes (I don't have a 100% reproducible scenario) it fails with the error below. It has to be noted that 99.9% of the time these procedures run smootly. The database user has write access to the exports directory.
The procedure:
TYPE PARTITION_NAME_TYPE IS RECORD (
PARTITION_NAME VARCHAR2(32)
);
TYPE PARTITION_LIST IS REF CURSOR RETURN PARTITION_NAME_TYPE;
PROCEDURE EXPORT_PARTITIONS (
P_TABLE_NAME IN VARCHAR2,
P_PARTITION_LIST IN PARTITION_LIST,
P_EXPORT_DIR IN VARCHAR2,
P_PARALLELISM_DEGREE IN NUMBER DEFAULT 1,
P_FILE_PATH OUT VARCHAR2,
P_RESULT OUT VARCHAR2,
P_RESULT_MSG OUT VARCHAR2)
IS
V_PUMP_HANDLE NUMBER; -- Data Pump job handle
V_PUMP_STATE VARCHAR2(255);
V_PUMP_FILE VARCHAR2(255);
V_PUMP_DIR VARCHAR2(255);
V_JOB_STATE VARCHAR2(4000);
V_STATUS KU$_STATUS1010;
V_LOGS KU$_LOGENTRY1010;
V_ROW PLS_INTEGER;
V_ERRORDETAILS VARCHAR2(4000);
V_PARTITION_LIST VARCHAR2(32767);
V_PARTITION_LIST_TEMP PARTITION_NAME_TYPE;
BEGIN
FETCH P_PARTITION_LIST INTO V_PARTITION_LIST_TEMP;
IF P_PARTITION_LIST%NOTFOUND THEN
P_RESULT := 'FAILED';
P_RESULT_MSG := 'No partitions to export';
RETURN;
END IF;
V_PARTITION_LIST := V_PARTITION_LIST_TEMP.PARTITION_NAME;
V_PUMP_FILE := V_PARTITION_LIST_TEMP.PARTITION_NAME;
LOOP
FETCH P_PARTITION_LIST INTO V_PARTITION_LIST_TEMP;
IF P_PARTITION_LIST%NOTFOUND THEN
V_PUMP_FILE := V_PUMP_FILE || '_TO_' || V_PARTITION_LIST_TEMP.PARTITION_NAME;
EXIT;
END IF;
V_PARTITION_LIST := V_PARTITION_LIST || ',' || V_PARTITION_LIST_TEMP.PARTITION_NAME;
END LOOP;
V_PUMP_HANDLE := DBMS_DATAPUMP.OPEN('EXPORT', 'TABLE');
DBMS_DATAPUMP.ADD_FILE(HANDLE => V_PUMP_HANDLE, FILENAME => V_PUMP_FILE || '.dbf' , DIRECTORY => P_EXPORT_DIR, FILETYPE => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE);
DBMS_DATAPUMP.ADD_FILE(V_PUMP_HANDLE, V_PUMP_FILE || '.log',P_EXPORT_DIR, NULL, DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE);
DBMS_DATAPUMP.DATA_FILTER (V_PUMP_HANDLE, 'PARTITION_LIST', V_PARTITION_LIST);
DBMS_DATAPUMP.SET_PARALLEL(V_PUMP_HANDLE, P_PARALLELISM_DEGREE);
DBMS_DATAPUMP.START_JOB (V_PUMP_HANDLE);
DBMS_DATAPUMP.WAIT_FOR_JOB(V_PUMP_HANDLE, V_PUMP_STATE);
DBMS_DATAPUMP.DETACH(V_PUMP_HANDLE);
P_RESULT := 'SUCCESS';
-- get actual path to Export Directory
SELECT directory_path INTO V_PUMP_DIR FROM ALL_DIRECTORIES WHERE directory_name = P_EXPORT_DIR;
P_FILE_PATH := V_PUMP_DIR || '/' || V_PUMP_FILE || '.dbf';
P_RESULT_MSG := 'Successfully exported ' || P_TABLE_NAME || '(' || V_PARTITION_LIST || ') as ' || V_PUMP_FILE || '.dbf';
EXCEPTION
WHEN OTHERS THEN
DBMS_DATAPUMP.GET_STATUS(V_PUMP_HANDLE, 8, 0, V_JOB_STATE, V_STATUS);
V_LOGS := V_STATUS.ERROR;
V_ROW := V_LOGS.FIRST;
LOOP
EXIT WHEN V_ROW IS NULL;
V_ERRORDETAILS := 'LOGLINENUMBER='||V_LOGS(V_ROW).LOGLINENUMBER || ' ERRORNUMBER='||V_LOGS(V_ROW).ERRORNUMBER || ' LOGTEXT='||V_LOGS(V_ROW).LOGTEXT;
V_ROW := V_LOGS.NEXT (V_ROW);
END LOOP;
P_RESULT_MSG := 'Failed to export partitions on ' || P_TABLE_NAME || '(' || V_PARTITION_LIST || ') : ' || SQLERRM || ' Details: (' || V_ERRORDETAILS || ')';
P_RESULT := 'FAILED';
DBMS_DATAPUMP.STOP_JOB(V_PUMP_HANDLE);
END EXPORT_PARTITIONS;
PROCEDURE EXPORT_PARTITIONS (
P_TABLE_NAME IN VARCHAR2,
P_NUM_DAYS_TO_KEEP IN NUMBER,
P_EXPORT_DIR IN VARCHAR2,
P_PARALLELISM_DEGREE IN NUMBER DEFAULT 1,
P_FILE_PATH OUT VARCHAR2,
P_RESULT OUT VARCHAR2,
P_RESULT_MSG OUT VARCHAR2)
IS
V_MIN_DAYS_TO_KEEP NUMBER := 1;
V_CNT NUMBER;
V_PARTITIONS PARTITION_LIST;
V_EXPORT_PARTITIONS_RESULT VARCHAR2(2000);
V_EXPORT_PARTITIONS_RESULT_MSG VARCHAR2(2000);
BEGIN
IF P_NUM_DAYS_TO_KEEP < V_MIN_DAYS_TO_KEEP THEN
RAISE_APPLICATION_ERROR(-20000, 'Cannot drop partitions less than ' || V_MIN_DAYS_TO_KEEP || ' days old.');
END IF;
SELECT COUNT(*)
INTO V_CNT
FROM USER_TABLES
WHERE TABLE_NAME = P_TABLE_NAME;
IF V_CNT = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Table Does Not Exist');
END IF;
OPEN V_PARTITIONS FOR SELECT partition_name
FROM USER_TAB_PARTITIONS
WHERE table_name = UPPER(P_TABLE_NAME)
AND
CASE
WHEN INSTR(PARTITION_NAME, 'MAXVALUE') = 0
THEN TO_DATE(SUBSTR(PARTITION_NAME, -8), 'YYYYMMDD')
END < TRUNC(SYSDATE) - P_NUM_DAYS_TO_KEEP
ORDER BY partition_name ASC;
EXPORT_PARTITIONS(P_TABLE_NAME, V_PARTITIONS, P_EXPORT_DIR, P_PARALLELISM_DEGREE, P_FILE_PATH, V_EXPORT_PARTITIONS_RESULT, V_EXPORT_PARTITIONS_RESULT_MSG);
CLOSE V_PARTITIONS;
IF INSTR(V_EXPORT_PARTITIONS_RESULT, 'SUCCESS') = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Cannot export partitions. Logs purging will not proceede. ' || V_EXPORT_PARTITIONS_RESULT_MSG);
END IF;
P_RESULT := 'SUCCESS';
P_RESULT_MSG := V_EXPORT_PARTITIONS_RESULT_MSG;
EXCEPTION
WHEN OTHERS THEN
P_RESULT_MSG := 'Failed To Export Partition on ' || P_TABLE_NAME || ': ' || SQLERRM;
P_RESULT := 'FAILED';
END EXPORT_PARTITIONS;
The error:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at * . *, line 208
ORA-39001: invalid argument value
What's on line 208 can be found below:
SQL> select text from all_source where type = 'PACKAGE BODY' and owner = '***' and name = '***' and line = '208';
TEXT
--------------------------------------------------------------------------------
P_RESULT_MSG := 'Failed to export partitions on ' || P_TABLE_NAME || '(' || V_
PARTITION_LIST || ') : ' || SQLERRM || ' Details: (' || V_ERRORDETAILS || ')';
After busting my head around for several days, I still haven't managed to find the problematic piece of code.
Edit #1
The caller of the procedure is a Hibernate CallableStatement. The declaration of the resultMsg on the java side is as follows:
call.registerOutParameter("resultMsg", Types.VARCHAR);
This calls the EXPORT_PARTITIONS(tableName, daysToKeep, exportDir, paralelismDegree) procedure, which then calls the second EXPORT_PARTITIONS procedure.

How to send the result of a select query to a message body of a mail in oracle 10G

CREATE OR REPLACE PROCEDURE STATUS_MAIL(FROM_MAIL IN VARCHAR2, TO_MAIL IN VARCHAR2)
is
v_From VARCHAR2(80) := FROM_MAIL;
v_Recipient VARCHAR2(80) := TO_MAIL;
v_Subject VARCHAR2(80) := 'EMPLOYEE STATUS';
v_Mail_Host VARCHAR2(30) := 'xx.xx.xxx.xxx';
v_Mail_Conn utl_smtp.Connection;
v_msg_body VARCHAR2(5000);
v_output VARCHAR2(5000);
BEGIN
/*Result always returns 42 rows*/
v_output := 'select empid,ename,mobile,dept from employee';
EXECUTE IMMEDIATE v_output into v_msg_body;
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, xx);
utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);
utl_smtp.Mail(v_Mail_Conn, v_From);
utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);
utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || UTL_TCP.crlf ||
'From: ' || v_From || UTL_TCP.crlf ||
'Subject: '|| v_Subject || UTL_TCP.crlf ||
'To: ' || v_Recipient || UTL_TCP.crlf ||
UTL_TCP.crlf || v_msg_body );
utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail: '||sqlerrm);
END;
Getting an error inconsistent datatypes
# EXECUTE IMMEDIATE v_output into v_msg_body
while executing the above procedure, please help me....
So in fact your real question is: "how do I aggregate multiple rows into a single string ?"
The answer is to use aggregate functions. Oracle has introduced listagg-function in 11gR2 that solves this problem nicely but in earlier releases one has to do a bit more work.
When you know the right keywords Google finds a plenty of great resources, e.g.
String Aggregation Techniques
listagg function in 11g release 2
the collect function in 10g
I have collected the following examples from the above mentioned resources. Hope this gives you a good starting point:
The table that will be queried:
create table foo (d1 number, d2 varchar2(10));
insert all
into foo values(1, 'a')
into foo values(2, 'b')
into foo values(3, 'c')
select 1 from dual;
commit;
Oracle 11gR2:
declare
v_str varchar2(32767);
begin
select listagg('key = ' || d1 || ' value = ' || d2, chr(10))
within group (order by d1)
into v_str
from foo;
dbms_output.put_line(v_str);
exception
when value_error then
dbms_output.put_line('Exception: trying to insert too many characters to a varchar2 variable.');
end;
/
Oracle 10g:
create or replace type str_list_t as table of varchar2(32676);
/
create function to_string (
nt_in in str_list_t,
delimiter_in in varchar2 default ','
) return varchar2 is
v_idx pls_integer;
v_str varchar2(32767);
v_dlm varchar2(10);
begin
v_idx := nt_in.first;
while v_idx is not null loop
v_str := v_str || v_dlm || nt_in(v_idx);
v_dlm := delimiter_in;
v_idx := nt_in.next(v_idx);
end loop;
return v_str;
end;
/
declare
v_str varchar2(32676);
begin
select to_string(cast(collect('key = ' || d1 || ' value = ' || d2) as str_list_t), chr(10))
into v_str
from foo;
dbms_output.put_line(v_str);
exception
when value_error then
dbms_output.put_line('Exception: trying to insert too many characters to a varchar2 variable.');
end;
/
Note how I'll catch value_error exception that will be raised if the aggregated string won't fit into the reserved varchar2 capacity.
Output of both examples:
key = 1 value = a
key = 2 value = b
key = 3 value = c

Resources