How to insert Oracle Collection data into CLOB Column of a table - sql

I have records in a collection variable. O want to insert all records into CLOB Column of table.
set serveroutput on;
declare
type ROW_DATA is table of varchar2(256) ;
ROW_D ROW_DATA;
begin
with DIFF_TAB_DATA as
(
select SOME_COLUMN from SOME_TABLE1
union all
select SOME_COLUMN from SOME_TABLE2
union all
select SOME_COLUMN from SOME_TABLE3
union all
select SOME_COLUMN from SOME_TABLE4
union all
select SOME_COLUMN from SOME_TABLE5
)
select SOME_COLUMN bulk collect into ROW_D from DIFF_TAB_DATA;
insert into CLOB_TAB values(ROW_D);
end;
But I am getting the error that local collection variable can not be used in insert statement.

Yes, that's a mismatch. The local collection types can't be replaced with the data type of the current value, those are totally unrelated. Rather call the collection along with an indexed set to extract the values stored in them. In order to perform this just replace
INSERT INTO clob_tab VALUES(row_d);
with
FORALL indx IN 1 .. row_d.COUNT
INSERT INTO clob_tab VALUES(row_d(indx));

Related

plsql Trigger to generate and insert values

I am trying to create a trigger to automatic generate and insert values with year and month (YYYYMM) into a table as inserts are made into another table.
Example:
As inserts are made into table 'original_table'
create table original_table
(opt_value char(2),
low_value varchar2(24),
high_value varchar2(24));
create table new_values
(id_values varchar2(24),
yr_month number(6));
Insert into original_table(opt_value,low_value,high_value) values ('EQ', '1111111111', '1111111111');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '2222222000', '2222222999');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '3333333350', '3333333399');
original_table
opt_value
low_value
high_value
EQ
1111111111
1111111111
BT
2222222000
2222222999
BT
3333333350
3333333399
Obs: Where EQ stands for 'equal' and BT 'between'. When 'EQ' just need to insert one of the values low or high don't matter, when 'BT' need to generate all the numbers between the two values and insert then into 'new_values' table.
table 'new_values' should get:
new_values
id_values
yr_month
1111111111
202111
2222222000
202111
2222222001
202111
2222222002
202111
...
...
2222222999
202111
3333333350
202111
3333333351
202111
...
...
3333333399
202111
I create a trigger which works, but i find it a little slow, and i don't like to use BEFORE INSERT statement, but can't use AFTER INSERT without getting mutating table error.
create or replace TRIGGER TRG_NAME
BEFORE INSERT
ON original_table
FOR EACH ROW
BEGIN
IF :NEW.opt_value = 'BT' THEN
INSERT INTO new_values (id_values, yr_month)
with tab123 (h_value, l_value, y_month)
as (select :NEW.high_value, cast(:NEW.low_value as number) , to_char(trunc(sysdate), 'YYYYMM')
from original_table
union all
select h_value, l_value +1, y_month
from tab123
where l_value < h_value)
select distinct l_value, y_month
from tab123;
ELSIF :NEW.opt_value = 'EQ' THEN
INSERT INTO new_values (id_values, yr_month) values
( :NEW.high_value, to_char(add_months(trunc(sysdate), -1), 'YYYYMM'));
END IF;
END;
Any tips in how to improve this code will be much appreciated.
You get a mutating table error, when you read from the table that is changing.
It's weird that you are getting it in an AFTER INSERT trigger and not with a BEFORE INSERT trigger. I would have assumed them to both result in the same error, because in both scenarios you read from the triggering table in your trigger. Well, this may have to do with only inserting one row. If you insert more rows at a time, you may get the error with both trigger variants.
In your case reading all the rows from the original_table in your trigger only produces duplicates anyway, that you fend off with DISTINCT. Instead, don't read from the table. It is not necessary. Read from DUAL instead, to get one row with the desired values:
with tab123 (h_value, l_value, y_month) as
(
select :new.high_value, cast(:new.low_value as number), to_char(sysdate, 'yyyymm')
from dual
union all
select h_value, l_value + 1, y_month
from tab123
where l_value < h_value
)
select l_value, y_month
from tab123;
This will get you rid of the mutating table error and also speed up the trigger.

How to execute SQL query stored in a table

I have a table having one of the columns that stores SQL query.
create table test1
(
name varchar(20),
stmt varchar(500)
);
insert into test1 (name, stmt)
values ('first', 'select id from data where id = 1;')
Data table is like:
create table data
(
id number,
subject varchar(500)
);
insert into data (id, subject) values (1, 'test subject1');
insert into data (id, subject) values (2, 'test subject2');
insert into data (id, subject) values (3, 'test subject2');
Now every time on insert in test1, I need to execute the query that gets inserted in stmt column of test1 and insert queried data to result table:
create table result
(
id number,
subject varchar(500)
);
For that I am writing a trigger that gets executed on every insert in test1 like as follows:
create or replace TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
sql_stmt VARCHAR2(500);
BEGIN
select stmt into sql_stmt from data where name = :NEW.name;
insert into result(id, subject)
select id,subject from data where id in ('stmt');
END;
Could you please let me know how to achieve this, above trigger is throwing error that I am not able to understand.
You can use a dynamic query in your trigger as follows:
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT" AFTER -- CHANGED IT TO AFTER AS NAME SUGGESTS
INSERT OR UPDATE ON TEST1
FOR EACH ROW -- REMOVED DECLARE SECTION
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO result
SELECT ID, SUBJECT FROM DATA WHERE ID IN ('
|| RTRIM(:NEW.STMT, ';')
|| ')';
-- SINGLE QUERY TO INSERT THE DATA
-- USED RTRIM AS STMT HAS ; AT THE END
END;
Cheers!!
Consider direct insertion :
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
BEGIN
insert into result(id, subject)
select id, subject from data where name = :NEW.name;
END;

How to SQL Query Large List of Tables using variable for table names?

I have a question about running a Oracle DB query on multiple tables. Is there a way to make the table names variables to be iterated as opposed to having to state each table name?
Background Example
There are a large number of tables (ex. TABLE_1...TABLE_100).
Each of these tables are listed in the NAME column of another table (ex. TABLE_LIST) listing an even larger number of tables along with TYPE (ex. "Account")
Each of these tables has columnn VALUE a boolean column, ACTIVE.
Requirements
Query the TABLE_LIST by TYPE = 'Account'
For Each table found, query that table for all records where column ACTIVE = 'N'
Results show table NAME and VALUE from each table row where ACTIVE = 'N'.
Any tips would be appreciated.
There is a low tech and a high tech way. I'll put them in separate answers so that people can vote for them. This is the high tech version.
Set up: Same as in low tech version.
CREATE TYPE my_row AS OBJECT (name VARCHAR2(128), value NUMBER)
/
CREATE TYPE my_tab AS TABLE OF my_row
/
CREATE OR REPLACE FUNCTION my_fun RETURN my_tab PIPELINED IS
rec my_row := my_row(null, null);
cur SYS_REFCURSOR;
BEGIN
FOR t IN (SELECT name FROM table_list WHERE table_type='Account') LOOP
rec.name := dbms_assert.sql_object_name(t.name);
OPEN cur FOR 'SELECT value FROM '||t.name||' WHERE active=''N''';
LOOP
FETCH cur INTO rec.value;
EXIT WHEN cur%NOTFOUND;
PIPE ROW(rec);
END LOOP;
CLOSE cur;
END LOOP;
END my_fun;
/
SELECT * FROM TABLE(my_fun);
NAME VALUE
TABLE_1 1
TABLE_3 3
There is a low tech and a high tech way. I'll put them in separate answers so that people can vote for them. This is the low tech version.
Set up:
CREATE TABLE table_1 (value NUMBER, active VARCHAR2(1) CHECK(active IN ('Y','N')));
CREATE TABLE table_2 (value NUMBER, active VARCHAR2(1) CHECK(active IN ('Y','N')));
CREATE TABLE table_3 (value NUMBER, active VARCHAR2(1) CHECK(active IN ('Y','N')));
INSERT INTO table_1 VALUES (1, 'N');
INSERT INTO table_1 VALUES (2, 'Y');
INSERT INTO table_3 VALUES (3, 'N');
INSERT INTO table_3 VALUES (4, 'Y');
CREATE TABLE table_list (name VARCHAR2(128 BYTE) NOT NULL, table_type VARCHAR2(10));
INSERT INTO table_list (name, table_type) VALUES ('TABLE_1', 'Account');
INSERT INTO table_list (name, table_type) VALUES ('TABLE_2', 'Something');
INSERT INTO table_list (name, table_type) VALUES ('TABLE_3', 'Account');
The quick and easy way is to use a query to generate another query. I do that quite often, especially for one off jobs:
SELECT 'SELECT '''||name||''' as name, value FROM '||name||
' WHERE active=''N'' UNION ALL' as sql
FROM table_list
WHERE table_type='Account';
SELECT 'TABLE_1' as name, value FROM TABLE_1 WHERE active='N' UNION ALL
SELECT 'TABLE_3' as name, value FROM TABLE_3 WHERE active='N' UNION ALL
You'll have to remove the last UNION ALL and execute the rest of the query. The result is
NAME VALUE
TABLE_1 1
TABLE_3 3

How do I store the resulting table from a SELECT query in a variable?

Suppose I have the following query, which returns a filtered table:
select column1,column2,column3 from table1 where table1.column1 = 'dada'
Further, suppose I want to store the resulting values in a variable so that I can use the result later on.
I have read that I can store a single value like so:
declare:
result varchar(20);
begin:
select column1 into result from table1 where table1.column1 = 'dada';
end;
But what about multiple values? There doesn't seem to be a "TABLE" or "DICTIONARY" datatype.
I think you need to use collections in oracle. Below code can help you.
Sample Code:
CREATE TABLE example
("S_ID" NUMBER(1,0) ,
"S_NAME" VARCHAR2(8 CHAR)
);
Insert into example (S_ID,S_NAME) values (1,'A');
Insert into example (S_ID,S_NAME) values (1,'B');
Insert into example (S_ID,S_NAME) values (1,'C');
Insert into example (S_ID,S_NAME) values (2,'A');
Insert into example (S_ID,S_NAME) values (2,'B');
Insert into example (S_ID,S_NAME) values (2,'C');
Insert into example (S_ID,S_NAME) values (3,'A');
Insert into example (S_ID,S_NAME) values (3,'B');
commit;
DECLARE
TYPE type_name IS TABLE OF example%rowtype;
var_type_name type_name;
BEGIN
SELECT *
BULK COLLECT INTO var_type_name
FROM example;
for r in var_type_name.first..var_type_name.last loop
dbms_output.put_line(var_type_name(r).s_id||','||var_type_name(r).s_name);
end loop;
END;
Help link:
https://blogs.oracle.com/oraclemagazine/working-with-collections
Use a view instead.
From W3Schools:
In SQL, a view is a virtual table based on the result-set of an SQL
statement.
Here's how you create one:
create view view1 as select column1,column2,column3 from table1 where
table1.column1 = 'dada'
create view tempTable as select column1,column2,... from table1 where
table1.column1 = 'dada'
declare:
result varchar(20);
begin:
select column1 into result from table1 where table1.column1 = 'dada'; // only work if you have single column in your result
end;
// For Multiple values use implicit cursor like below
FOR c_2151 in (
select column1 from table1 where table1.column1 = 'dada'
) LOOP
DBMS_OUTPUT.PUT_LINE('Display: '|| c_2151.column1);
END LOOP;

Oracle SQL random value from string set

In Oracle SQL 11g I am trying to fill a table with procedure. For some columns I need to take data randomly from predefined set of strings. How do I define such set and take data from it by random order?
You could use a cte and dbms_random.value. Something like:
with strings as (
select 'string1' as s from dual union all
select 'string2' as s from dual union all
select 'string3' as s from dual union all
select 'string4' as s from dual
)
select <col1>,
(select s
from (select s from strings order by dbms_random.value) s
where rownum = 1
) as RandomString
from dual;
Can you give this a try,it is working.
1.Insert the list of strings in a table (strings).
2.Create a function(RANDOM) to generate random number.
3.Create a procedure(PROC_STRING) to pick a string name from (STRINGS) table using the random number generated from function(RANDOM) and then insert into (NEW_TABLE)
PROGRAM:
--Table with list of string names
Create table strings (string_id number,string_name varchar2(2000) );
--Table to store new string names in random order
Create table new_table (string_id number,string_name varchar2(2000) );
--Function to generate random numbers
create or replace function random(p_number in number)
return number
is
a number;
begin
select dbms_random.value(1,10) into a
from dual;
a := floor(a);
return a;
end;
/
delete from strings;
delete from new_table;
insert into strings values(1,'abc');
insert into strings values(2,'def');
insert into strings values(3,'ghi');
insert into strings values(4,'abc 1');
insert into strings values(5,'def 1');
insert into strings values(6,'ghi 1');
insert into strings values(7,'abc 2');
insert into strings values(8,'def 2');
insert into strings values(9,'ghi 2');
insert into strings values(10,'xyz 3');
--Procedure to pick string names randomly from strings table and insert into new_table
create or replace procedure proc_string(p_no in number)
as
s_id number;
s_name varchar2(2000);
begin
select random(1) into s_id from dual;
select string_name into s_name from strings where string_id = s_id;
insert into New_table values(s_id,s_name);
dbms_output.put_line('insert successfully completed');
commit;
Exception when others
then dbms_output.put_line('ERROR:' || SQLCODE || ' ' || SQLERRM);
end;
/
commit;
EXECUTION:
--After executing the procedure for 3 times
SQL> exec proc_string(1);
insert successfully completed
PL/SQL procedure successfully completed.
-- Random string names got inserted into newtable
SQL> select * from new_table;
STRING_ID STRING_NAME
5 def 1
3 ghi
1 abc
Let me know if you questions.