pl sql replace multiple values in sequence - oracle

I have 2 columns in a table named Query and Values.
Query :
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values (l_Name,:l_Line,:l_Type,:l_Command,:l_Rule,:l_Client_ID,:l_Site_ID,l_Name)
Values : #1(2):20 #2(1):H #3(2):IF #4(27):FA - RETAIN OLD MASTER DATA #5(0): #6(0):
CName and SName use same value Manu.
I want to replace binded vars with values in next columns to get executable query.
What i want:
I want to write a procedure which get values from both columns of table to make a query and execute that query.
insert into address_MP (CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values ('Manu','20','H','IF','FA - RETAIN OLD MASTER DATA','','Manu')

Here's one option, which converts both query VALUES clause, as well as values themselves into rows - each in its own cursor loop, pairing bind variable and its value via their row number. CHR(39) is a single quote.
As the table most probably doesn't contain a single row (is it identified by some ID? You never told us), you'll have to adjust it, otherwise it won't work properly.
Test table:
SQL> select * from test;
QUERY
--------------------------------------------------------------------------------
C_VALUES
--------------------------------------------------------------------------------
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values (l_Name,:l_Line,:l_Type,:l_Command,:l_Rule,:l_Client_ID,:l_Site_ID,l_Name
)
#1(2):20 #2(1):H #3(2):IF #4(27):FA - RETAIN OLD MASTER DATA #5(0): #6(0):
SQL> set serveroutput on
Code & the result:
SQL> DECLARE
2 l_query test.query%TYPE;
3 l_name VARCHAR2 (30) := 'Manu';
4 BEGIN
5 SELECT query INTO l_query FROM test;
6
7 -- bind variables in QUERY
8 FOR cur_l
9 IN (SELECT ROW_NUMBER () OVER (ORDER BY lvl) rn, res l_val
10 FROM ( SELECT LEVEL lvl,
11 REGEXP_SUBSTR (res,
12 '[^,]+',
13 1,
14 LEVEL)
15 res
16 FROM (SELECT SUBSTR (query, INSTR (query, 'values')) res
17 FROM test)
18 CONNECT BY LEVEL <= REGEXP_COUNT (res, ':') + 1)
19 WHERE SUBSTR (res, 1, 1) = ':')
20 LOOP
21 -- values in VALUES
22 FOR cur_v
23 IN (SELECT rn, TRIM (SUBSTR (res, INSTR (res, ':') + 1)) c_val
24 FROM ( SELECT LEVEL rn,
25 REGEXP_SUBSTR (t.c_values,
26 '[^#]+',
27 1,
28 LEVEL)
29 res
30 FROM test t
31 CONNECT BY LEVEL <= REGEXP_COUNT (t.c_values, '#'))
32 WHERE rn = cur_l.rn)
33 LOOP
34 l_query :=
35 REPLACE (l_query,
36 cur_l.l_val,
37 CHR (39) || cur_v.c_val || CHR (39));
38 END LOOP;
39 END LOOP;
40
41 -- Put Manu into l_Name
42 l_query := REPLACE (l_query, 'l_Name', CHR (39) || l_name || CHR (39));
43
44 DBMS_OUTPUT.put_line (l_query);
45 END;
46 /
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values ('Manu','20','H','IF','FA - RETAIN OLD MASTER DATA','','','Manu')
PL/SQL procedure successfully completed.
SQL>

Related

unique output with select bulk and loop

I have data structure looking like this:
So in column_a I have figures form 1 to 5, but in column_b I have elements which a repeating. And I want to loop column_b elements so that only unique are shown.
Here my pl/sql:
SET SERVEROUTPUT ON SIZE 100000
DECLARE
var_column_name_a VARCHAR2(30);
var_column_name_b VARCHAR2(30);
BEGIN
SELECT
column_name_a,
column_name_b
BULK COLLECT
INTO
var_column_name_a,
var_column_name_b
FROM
table_name;
FOR i IN var_column_name_a.first..var_column_name_a.last LOOP
dbms_output.put_line(var_column_name_b(i));
END LOOP;
END;
/
My current output is this:
type_a
type_b
type_b
type_b
type_c
but I need this
type_a
type_b
type_c
How to achieve this?
No way that code you posted produces anything, it is invalid. You can't bulk insert into scalar variables, so either you faked code you wrote, or the output.
Anyway: to me, the simplest option seems to be a separate select of distinct values into another collection. Here's how:
Sample data:
SQL> SELECT * FROM test;
COLA COLB
---------- ------
1 type a
2 type b
3 type b
4 type b
5 type c
Procedure:
SQL> DECLARE
2 TYPE tr IS RECORD
3 (
4 cola NUMBER,
5 colb VARCHAR2 (10)
6 );
7
8 TYPE tt IS TABLE OF tr;
9
10 l_tab tt; --> for all rows in TEST table
11 l_tab_unique SYS.odcivarchar2list; --> just for COLB
12 BEGIN
13 SELECT cola, colb
14 BULK COLLECT INTO l_tab
15 FROM test;
16
17 SELECT DISTINCT colb
18 BULK COLLECT INTO l_tab_unique
19 FROM test;
20
21 DBMS_OUTPUT.put_line ('all rows -------------');
22
23 FOR i IN l_tab.FIRST .. l_tab.LAST
24 LOOP
25 DBMS_OUTPUT.put_line (l_tab (i).cola || ' ' || l_tab (i).colb);
26 END LOOP;
27
28 DBMS_OUTPUT.put_line ('unique rows ----------');
29
30 FOR i IN l_tab_unique.FIRST .. l_tab_unique.LAST
31 LOOP
32 DBMS_OUTPUT.put_line (l_tab_unique (i));
33 END LOOP;
34 END;
35 /
The result is:
all rows -------------
1 type a
2 type b
3 type b
4 type b
5 type c
unique rows ----------
type b
type c
type a
PL/SQL procedure successfully completed.
SQL>
First off a simple select distinct col_b bulk ... is sufficient for what you are asking for. But assuming you may want a list of the other column you can get it with LISTAGG function. Also for this you can use an older (but still valid) form of BULK COLLECT where each column in the select is collected into a independent collection. So you wind up with the structure:
select col_a, col_b
bulk collect
into collection_a, collection_b
... ;
Specifically here with LISTAGG for col_a: (see demo)
declare
type array_t is table of varchar2(256) ;
k_tab constant varchar2(1) := chr(09);
col_a_list array_t;
col_b_list array_t;
begin
select col_b, listagg( col_a, ',') within group (order by col_a) a_list
bulk collect
into col_b_list, col_a_list --<<< BULK COLLECT into separate collection
from t1
group by col_b
order by col_b;
dbms_output.put_line ('col_b') ;
dbms_output.put_line ('_____');
for list_cnt in 1 .. col_b_list.count
loop
dbms_output.put_line(col_b_list(list_cnt));
end loop;
dbms_output.put_line (' ') ;
dbms_output.put_line ('col_b' || k_tab || 'col_a_list') ;
dbms_output.put_line ('_____' || k_tab || '__________') ;
for list_cnt in 1 .. col_b_list.count
loop
dbms_output.put_line(col_b_list(list_cnt) || k_tab || col_a_list(list_cnt));
end loop;
end ;

E-mail result of 'select' statement having multiple rows as result set in ORACLE

I am new to ORACLE PL/SQL world. I am trying to figure out a way to calculate something as below.
Let's say you have a MASTER_TABLE as below :
SELECT * FROM MASTER_TABLE;
+----------+----------+------------------+-----------------------+
| SCHEMA | TABLE_NM | REQUIRED_COLUMNS | TABLE_FILTER |
+----------+----------+------------------+-----------------------+
| USER_SCH | A | A1,A2,A3 | EXAM_DT > SYSDATE - 1 |
| USER_SCH | B | B1,B2 | TRUNC(SYSDATE) |
+----------+----------+------------------+-----------------------+
I would like to generate SELECT query from above table such as below:
SELECT 'SELECT SCHEMA || '.' || TABLE_NM ||' WHERE '|| TABLE_FILTER FROM MASTER_TABLE;
Obviously, the result of above query would generate multiple select statements.
Now, I want to execute all such SELECT statements and send out the resultset via e-mail.
The tricky part is, the columns mentioned in the MASTER_TABLE varies (i.e. For table 'A' there can be 3 REQUIRED_COLUMNS to be selected, For table 'B' there can be 2 REQUIRED_COLUMNS to be selected - As shown in the MASTER_TABLE)
I have the e-mail utility ready which basically takes an argument as your_message and sends it out via e-mail.
Here is what I have tried :
Created CURSOR to generate such select statements.
Tried inserting the resultset (LIST OF SELECT QUERIES) to another temp table by concatenating the columns.
(i.e.
SELECT
'SELECT '
|| replace(required_columns, ',', '||'',''||')
|| ' AS MSG_BDY'
|| ' FROM '
|| schema
|| '.'
|| table_nm
|| ' WHERE '
|| table_filter
as my_select_stmt
FROM
master_table;
I am stuck after this.
Can you please help me out ? or is there any approach to achieve this ?.
Note : Tables mentioned in MASTER_TABLE can have 1 or more rows.
I don't have your tables so I used Scott's.
Master table:
SQL> select * From master_table;
SCHEM TABL REQUIRED_COLUMN TABLE_FILTER
----- ---- --------------- ----------------------
scott emp ename, job, sal hiredate < sysdate - 1
scott dept dname, loc deptno = 20
SQL>
Procedure which simulates your mailing procedure; I'll just display those values.
SQL> CREATE OR REPLACE PROCEDURE p_mail (par_result IN SYS.odcivarchar2list)
2 AS
3 BEGIN
4 FOR i IN par_result.FIRST .. par_result.LAST
5 LOOP
6 DBMS_OUTPUT.put_line (par_result (i));
7 END LOOP;
8 END;
9 /
Procedure created.
SQL>
Procedure you actually need; as you composed the select statement(s), now you only have to run them. In order to do so, use dynamic SQL (e.g. execute immediate):
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 retval SYS.odcivarchar2list;
3 BEGIN
4 FOR cur_r
5 IN (SELECT 'SELECT '
6 || REPLACE (required_columns, ',', '||'',''||')
7 || ' AS MSG_BDY'
8 || ' FROM '
9 || schema
10 || '.'
11 || table_nm
12 || ' WHERE '
13 || table_filter
14 AS my_select_stmt
15 FROM master_table)
16 LOOP
17 EXECUTE IMMEDIATE cur_r.my_select_stmt BULK COLLECT INTO retval;
18
19 -- you'd call your mailing procedure here
20 p_mail (retval);
21 END LOOP;
22 END;
23 /
SMITH,CLERK,920
ALLEN,SALESMAN,1600
WARD,SALESMAN,1250
JONES,MANAGER,2975
MARTIN,SALESMAN,1250
BLAKE,MANAGER,2850
CLARK,MANAGER,2450
SCOTT,ANALYST,3000
KING,PRESIDENT,10000
TURNER,SALESMAN,1500
ADAMS,CLERK,1300
JAMES,CLERK,950
FORD,ANALYST,3000
MILLER,CLERK,1300
RESEARCH,DALLAS
PL/SQL procedure successfully completed.
SQL>
[EDIT: what if you wanted to display 'null' for missing values?]
Well, that's a new moment - probably not very simple. See if this helps.
In order to help myself, I modified master_table and added ID column to uniquely identify every row. It'll be used to split required columns' list to rows, apply NVL to them, apply CAST to columns (because NVL complains if datatypes don't match), aggregate them back using listagg. As this is quite a lot to do, I'm going to create a view and use it instead of the table itself.
SQL> CREATE OR REPLACE VIEW v_master_table
2 AS
3 SELECT id,
4 schema,
5 table_nm,
6 LISTAGG ('NVL(cast(' || col || ' as varchar2(20)), ''null'')', '||'',''||')
7 WITHIN GROUP (ORDER BY lvl)
8 required_columns,
9 table_filter
10 FROM (SELECT id,
11 schema,
12 table_nm,
13 table_filter,
14 COLUMN_VALUE lvl,
15 TRIM (REGEXP_SUBSTR (required_columns,
16 '[^,]+',
17 1,
18 COLUMN_VALUE))
19 col
20 FROM master_table
21 CROSS JOIN
22 TABLE (
23 CAST (
24 MULTISET (
25 SELECT LEVEL
26 FROM DUAL
27 CONNECT BY LEVEL <=
28 REGEXP_COUNT (required_columns,
29 ',')
30 + 1) AS SYS.odcinumberlist)))
31 GROUP BY id,
32 schema,
33 table_nm,
34 table_filter;
View created.
For example, it now looks like this:
SQL> select * from v_master_table where id = 2;
ID SCHEM TABL REQUIRED_COLUMNS TABLE_FILTER
--- ----- ---- ------------------------------------------------------------------------------------- ------------
2 scott dept NVL(cast(dname as varchar2(20)), 'null')||','||NVL(cast(loc as varchar2(20)), 'null') deptno = 20
SQL>
The mailing procedure remains the same, no change.
Anonymous PL/SQL block is slightly changed - I removed REPLACE you previously used as view does it now; also, source is the view, not the table.
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> DECLARE
2 retval SYS.odcivarchar2list;
3 BEGIN
4 FOR cur_r
5 IN (SELECT 'SELECT '
6 || required_columns
7 || ' AS MSG_BDY'
8 || ' FROM '
9 || schema
10 || '.'
11 || table_nm
12 || ' WHERE '
13 || table_filter
14 AS my_select_stmt
15 FROM v_master_table)
16 LOOP
17 EXECUTE IMMEDIATE cur_r.my_select_stmt BULK COLLECT INTO retval;
18
19 -- you'd call your mailing procedure here
20 p_mail (retval);
21 END LOOP;
22 END;
23 /
CLARK,09.06.1981,null
KING,17.11.1981,null
MILLER,23.01.1982,null
RESEARCH,DALLAS
PL/SQL procedure successfully completed.
SQL>

oracle delete data from table with rownum

I have a procedure that deletes the data from table1. Five instances of the procedure running the same time and all ran without any error/exception.
But still, there are many records in the table1 where EndTime <= v_purgedate. Please help.
declare
rowcount1 NUMBER;
begin
LOOP
delete table1 eh
where eh.EndTime <= v_purgedate
and rownum <= 30000 ;
rowcount1 := rowcount1 + sql%rowcount;
commit;
exit when rowcount1=0;
rowcount1=0;
END LOOP;
end;
/
You are trying to do what is called "do it yourself parallelism", as opposed to the parallel processing that Oracle can do by itself if you ask nicely.
There is a package called DBMS_PARALLEL_EXECUTE that does this for you. You split the data into chunks (based on ROWIDs or numbers), then call the RUN_TASK procedure to process each chunk, either serially or in parallel. There is one commit per chunk.
Here is a demonstration that deletes 30,000 rows per commit. First, test data:
SQL> create table table1(endtime) as
2 select trunc(sysdate) - level/24 from dual
3 connect by level <= 240000;
Table TABLE1 created.
SQL> insert into table1 select * from table1;
240,000 rows inserted.
SQL> insert into table1 select * from table1;
480,000 rows inserted.
SQL> insert into table1 select * from table1;
960,000 rows inserted.
SQL> select count(*) from table1;
COUNT(*)
----------
1920000
Now, here is the code:
SQL> declare
2 l_task_name varchar2(128) := 'Delete_TABLE1';
3 l_sql_chunk clob := q'§
4 select date '2100-01-01' - min(endtime) start_id,
5 date '2100-01-01' - max(endtime) end_id,
6 chunk
7 from (
8 select endtime,
9 ceil(row_number() over(order by endtime) / 30000) chunk
10 from table1 where endtime < sysdate - 8000
11 )
12 group by chunk
13 §';
14 l_sql_run clob := q'§
15 delete from table1
16 where endtime between
17 date '2100-01-01' - :start_id and
18 date '2100-01-01' - :end_id
19 §';
20 l_boolean boolean := false;
21 task_not_found EXCEPTION;
22 PRAGMA EXCEPTION_INIT(task_not_found, -29498);
23 begin
24 begin
25 DBMS_PARALLEL_EXECUTE.DROP_TASK (l_task_name);
26 exception when task_not_found then null;
27 end;
28 DBMS_PARALLEL_EXECUTE.CREATE_TASK (l_task_name);
29 DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_SQL (
30 task_name => l_task_name,
31 sql_stmt => l_sql_chunk,
32 by_rowid => l_boolean
33 );
34 DBMS_PARALLEL_EXECUTE.RUN_TASK (
35 task_name => l_task_name,
36 sql_stmt => l_sql_run,
37 language_flag => 1,
38 parallel_level => 0 -- 0 for serial, 2+ for parallel
39 );
40 end;
41 /
you have an endless Loop, because rowcount1 will never be 0 when your table contains data that will be deleted.
i think what you what is
declare
rowcount1 NUMBER;
begin
LOOP
delete table1 eh
where eh.EndTime <= v_purgedate -- you should also initialize a variable
and rownum <= 30000 ;
--rowcount1 := rowcount1 + sql%rowcount;
commit;
exit when sql%rowcount = 0;
rowcount1=0;
END LOOP;
end;
/
When you declare the variable rowcount1, you do not assign any number to it, so its value is NULL. Afterwards, when you add a number to NULL the result is always NULL. This is surely not what you want. To illustrate:
SQL> declare
2 rowcount1 NUMBER;
3 begin
4 dbms_output.put_line('<'||rowcount1||'>');
5 rowcount1 := rowcount1 + 1;
6 dbms_output.put_line('<'||rowcount1||'>');
7 end;
8 /
<>
<>
PL/SQL procedure successfully completed.
It seems your code will go in an endless loop. You say that is not the case, so clearly you are not showing us any real code.
Anyway, this is not a good way to do what you want. I'll post another answer saying why.
Regards, Stew Ashton
It's a easy code for delete data in a table .
Try it.
declare
cursor c is
select * from table1 where rownum<=30000;
begin
for i in c loop
delete from table1 where EndTime <= v_purgedate;
end loop;
commit;
end;

Wrong calculation for daily partitions

Oracle : 11.2.0.2
I'm trying to drop monthy and daily partitions using a script. This works fine for monthly partitions but not for daily partitions. Below is the error I see in the log. Day of the month is becoming zero when calculating.
2013-08-0|SYS_P328538|2|YES
DECLARE
*
ERROR at line 1:
ORA-01847: day of month must be between 1 and last day of month
ORA-06512: at line 43
Here below is the script. I think highvalue date is miscalculated.
SQL> DECLARE
2 CURSOR tab_part_cur IS
3 select PARTITION_POSITION, PARTITION_NAME,HIGH_VALUE,INTERVAL from dba_tab_partitions where table_name = 'MO_USAGEDATA'
and table_owner = 'WSMUSER17'
order by PARTITION_POSITION;
4 tab_part_rec tab_part_cur%ROWTYPE;
5 lHighValue LONG;
6 strPartitionLessThanDate VARCHAR2(100);
7 dtTestDate DATE;
8 DaysInPast NUMBER;
9 SQLstr varchar2(100);
10 strIntervalType varchar2(1000);
11 strRunType varchar2(20);
12 BEGIN
13 strRunType := 'DRY_RUN';
14 select INTERVAL into strIntervalType from dba_part_tables where table_name ='MO_USAGEDATA' and owner = 'WSMUSER17';
15 strIntervalType := REGEXP_SUBSTR(strIntervalType, '''[^'']+''');
16 DBMS_OUTPUT.PUT_LINE(strIntervalType);
17 CASE
18 WHEN strIntervalType = '''DAY''' THEN
19 DBMS_OUTPUT.PUT_LINE('Interval type = '||strIntervalType);
20 -- dtTestDate := CURRENT_DATE - 7 - 1; Offset adjustment if necessary
21 dtTestDate := CURRENT_DATE - 7;
22 DBMS_OUTPUT.PUT_LINE('Test Date = '||dtTestDate);
23 WHEN strIntervalType = '''MONTH''' THEN
24 DBMS_OUTPUT.PUT_LINE('Interval type = '||strIntervalType);
25 -- dtTestDate := CURRENT_DATE - 90;
26 dtTestDate := ADD_MONTHS(current_date,- 7);
27 DBMS_OUTPUT.PUT_LINE('TestDate = '||dtTestDate);
28 ELSE
29 DBMS_OUTPUT.PUT_LINE('Unexpected interval, exiting.');
30 GOTO EXIT;
31 END CASE;
32 OPEN tab_part_cur;
33 LOOP
34 FETCH tab_part_cur INTO tab_part_rec;
35 EXIT WHEN tab_part_cur%NOTFOUND;
36 DBMS_OUTPUT.PUT_LINE(tab_part_cur%ROWCOUNT);
37 lHighValue := tab_part_rec.high_value;
38 /* This next line seems redundant but is needed for conversion quirk from LONG to VARCHAR2
39 */
40 strPartitionLessThanDate := lHighValue;
41 strPartitionLessThanDate := substr(strPartitionLessThanDate, 11, 10);
42 DBMS_OUTPUT.PUT_LINE(strPartitionLessThanDate ||'|'|| tab_part_rec.partition_name ||'|'|| tab_part_rec.partition_position ||'|'|| tab_part_rec.interval);
43 DBMS_OUTPUT.PUT_LINE(TO_DATE(strPartitionLessThanDate, 'YYYY-MM-DD') ||'******'||dtTestDate);
44 IF TO_DATE(strPartitionLessThanDate, 'YYYY-MM-DD') < dtTestDate AND tab_part_rec.partition_name <> 'PART_MINVALUE
' THEN
45 SQLstr := 'ALTER TABLE WSMUSER17.MO_USAGEDATA DROP PARTITION '||tab_part_rec.partition_name ||' update Global indexes';
46 DBMS_OUTPUT.PUT_LINE('Targeted Partition !!!!!!!!');
47 IF strRunType = 'LIVE_RUN' THEN
48 DBMS_OUTPUT.PUT_LINE('Dropping Partition !!!!!!!!');
49 execute immediate SQLstr;
50 END IF;
51 END IF;
52 END LOOP;
53 CLOSE tab_part_cur;
54 << EXIT >>
55 DBMS_OUTPUT.PUT_LINE('Partition purge complete');
56 END;
57 /
'DAY'
Interval type = 'DAY'
Test Date = 03-SEP-13
1
2012-06-1|PART_MINVALUE|1|NO
01-JUN-12******03-SEP-13
2
2013-08-0|SYS_P328538|2|YES
DECLARE
*
ERROR at line 1:
ORA-01847: day of month must be between 1 and last day of month
ORA-06512: at line 43
I'm trying to keep lat 7 partitions in the daily partitioned table and drop the rest of the partitions. But its not dropping them.
Ok, I created the table, inserted some data and ran some of your queries and you've got something wrong with your substring:
SQL> CREATE TABLE "MO_USAGEDATA" (
2 "REQUESTDTS" TIMESTAMP (9) NOT NULL ENABLE
3 )
4 partition by range ("REQUESTDTS") INTERVAL(NUMTODSINTERVAL(1,'DAY'))
5 (partition PART_MINVALUE values less than(TIMESTAMP '2012-06-18 00:00:00'));
Table created
SQL> INSERT INTO MO_USAGEDATA
2 (SELECT SYSDATE + ROWNUM FROM dual CONNECT BY LEVEL <= 30);
30 rows inserted
SQL> SELECT high_value, INTERVAL
2 FROM all_tab_partitions
3 WHERE table_name = 'MO_USAGEDATA'
4 AND table_owner = USER
5 ORDER BY PARTITION_POSITION;
HIGH_VALUE INTERVAL
------------------------------------ ---------
[...]
TIMESTAMP' 2013-09-30 00:00:00' YES
TIMESTAMP' 2013-10-01 00:00:00' YES
TIMESTAMP' 2013-10-02 00:00:00' YES
[...]
SQL> SELECT substr('TIMESTAMP'' 2013-10-02 00:00:00''', 11, 10) FROM dual;
SUBSTR('TIMESTAMP''2013-10-020
------------------------------
2013-10-0
As you can see you're off by one character. It works with DATE columns, but for TIMESTAMP partitionning, you'll need to adjust the offset.

how to append values to a oracle type

how can I append (insert) 3 or 4 different values to an oracle type and then later open it up for a cursor.
For example (pseudo):
insert into mytype select 1 from dual;
insert into mytype select 3 from dual;
insert into mytype select 5 from dual;
open cursor_1 for select * from table(mytype);
Is this possible to do in pl/sql?
I know this is trivial and can be combined into one query but my real need is to have different queries and keep appending the results to mytype.
Assuming you mean you have a custom SQL type (presumably a nested table type), and a PL/SQL variable of that type: I don't believe you can INSERT into it, and I don't think you can SELECT into it in a way that would append to the collection.
You can select into a scalar variable, then append it to the collection procedurally.
SQL> create type mytype as table of integer;
2 /
Type created.
SQL> set serveroutput on
SQL> l
1 declare
2 mytable mytype := mytype();
3 cursor_1 sys_refcursor;
4 x integer;
5 procedure append_to_table( t IN OUT mytype, y IN INTEGER)
6 is
7 begin
8 t.extend();
9 t(t.COUNT) := y;
10 end append_to_table;
11 begin
12 select 1 into x from dual;
13 append_to_table( mytable, x );
14 select 3 into x from dual;
15 append_to_table( mytable, x );
16 select 5 into x from dual;
17 append_to_table( mytable, x );
18 open cursor_1 for select * from table(cast(mytable as mytype));
19 fetch cursor_1 into x;
20 dbms_output.put_line(x);
21 fetch cursor_1 into x;
22 dbms_output.put_line(x);
23 fetch cursor_1 into x;
24 dbms_output.put_line(x);
25 close cursor_1;
26* end;
SQL> /
1
3
5
PL/SQL procedure successfully completed.
Manipulating PL/SQL collections is a lot easier since 10g, which gave us some SET operators we can use with them.
As you know, to employ the TABLE() function means we have to use a SQL type...
SQL> create or replace type nums_nt as table of number
2 /
Type created.
SQL>
The following block populates a collection with some numbers, which it uses in a FOR loop. Then it executes a another query to populate a second collection. The second collection is added to the first collection using the MULTISET UNION syntax. Unlike the the SQL UNION operator, this implementation does not winnow duplicates (we can use MULTISET UNION DISTINCT for that). The code finishes off by looping through the first collection again, to prove that it contains both sets of numbers.
SQL> set serveroutput on
SQL>
SQL> declare
2 master_nos nums_nt;
3 fresh_nos nums_nt;
4 begin
5
6 dbms_output.put_line ('get some numbers, print some names');
7
8 select id
9 bulk collect into master_nos
10 from t23
11 where name not in ( select upper(name) from t_doctors )
12 and name not in ( select upper(name) from t_kids );
13
14 for r in ( select t23.name
15 from t23
16 join ( select * from table(master_nos)) sq
17 on t23.id = sq.column_value
18 )
19 loop
20 dbms_output.put_line (r.name);
21 end loop;
22
23 dbms_output.put_line ('get more numbers, print all names');
24
25 select id
26 bulk collect into fresh_nos
27 from t23
28 where name in ( select upper(name) from t_doctors );
29
30 master_nos := master_nos
31 MULTISET UNION
32 fresh_nos;
33
34 for r in ( select t23.name
35 from t23
36 join ( select * from table(master_nos)) sq
37 on t23.id = sq.column_value
38 )
39 loop
40 dbms_output.put_line (r.name);
41 end loop;
42
43 end;
44 /
get some numbers, print some names
CAT
PINNER BLINN
LORAX
MR KNOX
FOX IN SOCKS
get more numbers, print all names
CAT
PINNER BLINN
LORAX
MR KNOX
FOX IN SOCKS
DR SINATRA
DR FONZ
PL/SQL procedure successfully completed.
SQL>

Resources