Creating a [materialised]view from generic data in Oracle/Mysql - sql

I have a generic datamodel with 3 tables
CREATE TABLE Properties
(
propertyId int(11) NOT NULL AUTO_INCREMENT,
name varchar(80) NOT NULL
)
CREATE TABLE Customers
(
customerId int(11) NOT NULL AUTO_INCREMENT,
customerName varchar(80) NOT NULL
)
CREATE TABLE PropertyValues
(
propertyId int(11) NOT NULL,
customerId int(11) NOT NULL,
value varchar(80) NOT NULL
)
INSERT INTO Properties VALUES (1, 'Age');
INSERT INTO Properties VALUES (2, 'Weight');
INSERT INTO Customers VALUES (1, 'Bob');
INSERT INTO Customers VALUES (2, 'Tom');
INSERT INTO PropertyValues VALUES (1, 1, '34');
INSERT INTO PropertyValues VALUES (2, 1, '80KG');
INSERT INTO PropertyValues VALUES (1, 2, '24');
INSERT INTO PropertyValues VALUES (2, 2, '53KG');
What I would like to do is create a view that has as columns all the ROWS in Properties and has as rows the entries in Customers. The column values are populated from PropertyValues.
e.g.
customerId Age Weight
1 34 80KG
2 24 53KG
I'm thinking I need a stored procedure to do this and perhaps a materialised view (the entries in the table "Properties" change rarely).
Any tips?

It's easy enough to generate a view with dynamic SQL:
create or replace procedure gen_view
as
cols_stmt varchar2(32767);
from_stmt varchar2(32767);
subq_name varchar2(30);
begin
for r in ( select * from properties
order by propertyid )
loop
subq_name := 'pv_'||trim(to_char(r.propertyid));
cols_stmt := cols_stmt || ', '|| subq_name ||'.value as '||r.name;
from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
||trim(to_char(r.propertyid))||') '||subq_name
||' on '||subq_name||'.customerid = customers.customerid';
end loop;
execute immediate 'create or replace view eav_view as select customers.customerid, customers.customername'
|| cols_stmt
|| ' from customers '
|| from_stmt;
end gen_view;
/
Here's it working:
SQL> exec gen_view
PL/SQL procedure successfully completed.
SQL> select * from eav_view
2 /
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
1
Bob
34
80KG
2
Tom
24
53KG
SQL>
Let's create a new property and insert values for it for some of the customers...
SQL> insert into properties values (3, 'FavouriteIceCream')
2 /
1 row created.
SQL> insert into propertyvalues values (3, 1, 'Cherry Garcia')
2 /
1 row created.
SQL> exec gen_view
PL/SQL procedure successfully completed.
SQL> select * from eav_view
2 /
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
SQL>
"I'm thinking I need a stored
procedure to do this and perhaps a
materialised view (the entries in the
table "Properties" change rarely)."
The problem is, Properties are going to change, and I'm guessing you will have no oversight of when that happens. So you are going to find it very hard to apply the changes to a materialized view. This matters because changing the projection of a materialized view necessitates dropping it. So it's quite difficult to do this without an interruption to service. A similar consideration applies to the regular view , but the outage is almost zero.
If you do want to convert the view statement into a materialized view note that Oracle doesn't seem to like the ANSI-92 syntax when it comes to materialized views (it hurls ORA-12054). I'm not sure why that should be, but the problem went away when I changed to the older joining technique, which is annoying because the outer join syntax is clunkier.
A solution without the need to re-create database objects would be to use the dynamic SQL in a function which returns a Ref Cursor, which maps to a JDBC ResultSet:
create or replace function get_eav_view
return sys_refcursor
as
cols_stmt varchar2(32767);
from_stmt varchar2(32767);
subq_name varchar2(30);
return_value sys_refcursor;
begin
for r in ( select * from properties
order by propertyid )
loop
subq_name := 'pv_'||trim(to_char(r.propertyid));
cols_stmt := cols_stmt || ','|| subq_name ||'.value as '||r.name;
from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
||trim(to_char(r.propertyid))||') '||subq_name
||' on '||subq_name||'.customerid = customers.customerid';
end loop;
open return_value for
'select customers.customerid, customers.customername'
|| cols_stmt
|| ' from customers '
|| from_stmt;
return return_value;
end get_eav_view;
/
This will always return the latest projection:
SQL> var rc refcursor
SQL> exec :rc := get_eav_view
PL/SQL procedure successfully completed.
SQL> print rc
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
SQL>
Now, if we add a new property it gets picked up immediately:
SQL> insert into properties values (4, 'StarSign')
2 /
1 row created.
SQL> insert into propertyvalues values (4, 2, 'Aries')
2 /
1 row created.
SQL> exec :rc := get_eav_view
PL/SQL procedure successfully completed.
SQL> print rc
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
STARSIGN
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
Aries
SQL>

Related

How can i make uniqueness in a sql table with using columns?

in my table there is no unique column , but i need to make uniqueness with using columns , adding a new column is worst option , i need to do this something like concat two or more columns how can i do that ?
i need to make uniqeness in my sql table with using two or more columns , adding a new columns is worst option , i need a solution which is concat two or more columns something like that how can i do that ?
Oracle
There is a way to check for "uniqueness" if you compare distinct values of different field combinations with total number of rows. In below example the score 0 (zero) means uniqueness:
WITH
tbl AS
(
Select 1 "COL_1", 'AXC' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 3 "COL_1", 'AXC' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 3 "COL_1", 'DEF' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 1 "COL_1", 'DEF' "COL_2", 'xxxx yyyy' "COL_3" From Dual
)
Select
Count(*) "TOTAL",
Count(*) - Count(DISTINCT COL_1) "COL_1_SCORE",
Count(*) - Count(DISTINCT COL_2) "COL_2_SCORE",
Count(*) - Count(DISTINCT COL_3) "COL_3_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_2) "COL_1-2_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_3) "COL_1-3_SCORE",
Count(*) - Count(DISTINCT COL_2 || COL_3) "COL_2-3_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_2 || COL_3) "COL_1-2-3_SCORE"
From
tbl
/* R e s u l t:
TOTAL COL_1_SCORE COL_2_SCORE COL_3_SCORE COL_1-2_SCORE COL_1-3_SCORE COL_2-3_SCORE COL_1-2-3_SCORE
---------- ----------- ----------- ----------- ------------- ------------- ------------- ---------------
4 2 2 2 0 1 1 0
*/
You can combine a handful of columns into a unique row id with the following SQL code.
I have done this when making data sets for machine learning in BigQuery.
select distinct
cast(to_hex(SHA1(concat(column1, column2, column3, column4))) as string) as observation_id
from table
Of course you need to check beforehand, that the combination of these columns produces unique rows.
One option is to create a unique index on set of columns you want to enforce uniqueness on.
For example:
create unique index ui1_test on test (id, name, address);
Doing so, users won't be able to insert new rows (or update existing ones) to a combination (of these columns) that already exists in the table as Oracle will raise an error.
I just implemented the following to make customer_id a unique column by combining a random number, part of a SYSTIMESTAMP and a sequence NUMBER. To make it difficult to decipher and shorter in length I ran in through a function. I also store the origin (seed) that was used to create the unique column.
As for the first_name and last_name I use so it makes sense in the example as there are names associated with customers.
CREATE OR REPLACE PACKAGE mf_names IS
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2;
FUNCTION random_last_name RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY mf_names IS
first_names_male SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Tom', 'Andrew', 'Paul', 'Peter', 'Keith', 'Mark', 'Solomon', 'Joseph', 'John', 'Melvin', 'Harry', 'Barry', 'Larry', 'Gary', 'Jeffrey', 'karl'
);
first_names_female SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Alice', 'Anna', 'Barbara', 'Carol', 'Debra', 'Madison', 'Faith', 'Cheryl', 'Beth', 'Kathy', 'Jill', 'Grayce', 'Lynn', 'Roz', 'Deena', 'Laura', 'Sophia'
);
last_names SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Cooper', 'Dimeo', 'Spatafore', 'Jones', 'Coralnick', 'Torchiano', 'Fazio', 'Behrens', 'Lebowitz','Stern', 'Malden', 'Kramer','Stein', 'Tessio', 'Weinreb', 'Dillon', 'Rucker', 'Silverberg', 'Aarron', 'Kern', 'Saladino', 'Rice', 'Sanford', 'Orr', 'Roth'
);
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2
IS
BEGIN
IF UPPER(gender) LIKE 'M%' THEN
RETURN first_names_male(FLOOR(DBMS_RANDOM.VALUE(1, first_names_male.COUNT + 1)));
ELSIF UPPER(gender) LIKE 'F%' THEN
RETURN first_names_female(FLOOR(DBMS_RANDOM.VALUE(1, first_names_female.COUNT + 1)));
ELSIF DBMS_RANDOM.VALUE(0, 100) < percentage_mf THEN
RETURN random_first_name('M');
ELSE
RETURN random_first_name('F');
END IF;
END;
FUNCTION random_last_name RETURN VARCHAR2
IS
BEGIN
RETURN last_names(FLOOR(DBMS_RANDOM.VALUE(1, last_names.COUNT + 1)));
END;
END;
/
CREATE TABLE CUSTOMERS (
customer_id VARCHAR2 (20),
seed NUMBER,
first_name VARCHAR2 (20),
last_name VARCHAR2 (20));
create sequence customer_seq start with 1000000 minvalue 1000000 maxvalue 9999999 cycle;
create or replace function base34(p_num number) return varchar2 is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := p_num;
l_str varchar2(38);
begin
loop
l_str := substr(l_dig,mod(l_num,34)+1,1) || l_str ;
l_num := trunc(l_num/34);
exit when l_num = 0;
end loop;
return l_str;
end;
/
create or replace function dec34(p_str varchar2) return number is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := 0;
begin
for i in 1 .. length(p_str) loop
l_num := l_num * 34 + instr(l_dig,upper(substr(p_str,i,1)))-1;
end loop;
return l_num;
end;
/
create or replace trigger customer_trg
before update on customers for each row
begin
if ( updating('customer_id') )
then
raise_application_error(-20000,'Cant Update customer_id');
end if;
if ( updating('seed') )
then
raise_application_error(-20001,'Cant Update seed');
end if;
end;
/
DECLARE
seed NUMBER;
begin
for i in 1 .. 10 loop
seed := (to_number(trunc(dbms_random.value(1000,9999))|| to_char(systimestamp,'FFSS')||customer_seq.nextval));
INSERT into customers(
customer_id,
seed,
first_name,
last_name
) VALUES (
base34(seed),
seed,
mf_names.random_first_name(),
mf_names.random_last_name()
);
end loop;
end;
/
SELECT * from customers
CUSTOMER_ID SEED FIRST_NAME LAST_NAME
BJGEYVDR5HSBEL8 3754328637000441000120 Cheryl Stern
B8FPDSMR0WQSTNZ 4886346472000441000121 Roz Dillon
00KGDZARBUZTCB6 5711346658000441000122 Solomon Kern
KBMP0WU4VQ76T5 1058346784000441000123 Grayce Lebowitz
0EXWKL06SC2E3R6 6080346899000441000124 karl Jones
BM9XSDNX7CWNNZC 4128347038000441000125 Alice Sanford
01SKXFTE18JTT5M 5981347154000441000126 Cheryl Saladino
B53A6Y19CF7B54F 4164347334000441000127 John Stein
B3XMHNKT9LDLSDA 3726347470000441000128 Joseph Lebowitz
B7FT0265MYGJUTP 4643347602000441000129 Debra Rice
select to_char(seed) from customers where rownum = 1
union all
select base34(seed) from customers where rownum = 1
union all
select to_char(dec34(base34(seed))) from customers where rownum = 1;
TO_CHAR(SEED)
3754328637000441000120
BJGEYVDR5HSBEL8
3754328637000441000120
select base34(seed),
dump(base34(seed)) from customers where rownum = 1
BASE34(SEED) DUMP(BASE34(SEED))
BJGEYVDR5HSBEL8 Typ=1 Len=15: 66,74,71,69,89,86,68,82,53,72,83,66,69,76,56

How do I loop through a row while using a cursor

create table ranks (
rank varchar(20)
);
create table people (
name varchar(20)
);
insert into people values('Sam', 'Bob', 'Tim');
declare cursor c1 is (select substr(name, -1) from people)
begin
for i in c1
loop
update ranks
set rank = 'S'
where i = 'S';
end loop;
end;
Hello, I am trying to use the last letter of the people table to decide who gets the S rank, but it isn't working. I keep getting - expression is of wrong type - error. Please help.
Data model looks wrong. That should be only one table with two columns.
SQL> CREATE TABLE people
2 (
3 name VARCHAR2 (20),
4 RANK VARCHAR2 (1)
5 );
Table created.
SQL> INSERT INTO people (name) VALUES ('Sam');
1 row created.
SQL> INSERT INTO people (name) VALUES ('Bob');
1 row created.
SQL> INSERT INTO people (name) VALUES ('Tim');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM people;
NAME RANK
-------------------- -----
Sam
Bob
Tim
SQL>
Then, you don't need PL/SQL - a simple UPDATE will do. However, code you posted doesn't make much sense either - substr(name, -1) selects the last letter; nobody has a name that ends with an S so - no rows will ever be updated (at least, not for sample data). That's why I modified it to use the 1st letter.
SQL> UPDATE people
2 SET RANK = 'S'
3 WHERE SUBSTR (name, 1, 1) = 'S';
1 row updated.
SQL> SELECT * FROM people;
NAME R
-------------------- -
Sam S
Bob
Tim
SQL>
If it has to be PL/SQL (because you're learning it), then you'd
SQL> ROLLBACK;
Rollback complete.
SQL> BEGIN
2 FOR cur_r IN (SELECT name FROM people)
3 LOOP
4 UPDATE people
5 SET RANK = 'S'
6 WHERE name = cur_r.name
7 AND SUBSTR (name, 1, 1) = 'S';
8 END LOOP;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM people;
NAME RANK
-------------------- ----
Sam S
Bob
Tim
SQL>
insert into people values('Sam', 'Bob', 'Tim');
Will fail as you only have one column and not three. You want to either use multiple inserts:
insert into people (name) values('Sam');
insert into people (name) values('Bob');
insert into people (name) values('Tim');
Or, use an INSERT ... SELECT ...
insert into people (name)
SELECT 'Sam' FROM DUAL UNION ALL
SELECT 'Bob' FROM DUAL UNION ALL
SELECT 'Tim' FROM DUAL;
Then you want something like:
begin
for i in (select substr(name, -1) AS last_character from people)
loop
update ranks
set rank = 'S'
where i.last_character = 'S';
end loop;
end;
/
But that can be simplified to get rid of the cursor and use a single UPDATE statement:
UPDATE ranks
SET rank = 'S'
WHERE EXISTS(
SELECT 1
FROM people
WHERE name LIKE '%S'
);
But neither of those will do anything as:
The ranks table contains zero rows.
None of the people have a name ending in S.
If you fix both of those then you will just end up updating every row in the ranks table as there is no relationship between a person and a rank.
db<>fiddle here
A simplified version of using cursors is to declare the cursor and then use it
multiple times if required later. This way opening similar dataset in multiple
cursors can be avoided. A good practice to develop when working on code
intensive procedures.
SQL> Declare
cur_people is SELECT name FROM people;
BEGIN
FOR cur_r IN cur_people
LOOP
UPDATE people
SET RANK = 'S'
WHERE name = cur_r.name
AND SUBSTR (name, 1, 1) = 'S';
END LOOP;
END;
/

Is there any possibility of creating view or table based on the output of a dynamically opened ref cursor?

I have learnt how to return a dynamically opened ref cursor. Now based on the output, I want to create a table or a view.
Input table:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
Records
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
Query output
sales_name 10-MAR-2019 11-MAR-2019 12-MAR-2019
Norb 10000 5000 1000
Bert 5000 13000 4000
Alba 4000 0 2000
Rob 200000 80000 40000
Now the result should be saved in a table or a view. I have learnt how to return a dynamically opened ref cursor so far.
------Here is the procedure I used -----------
create or replace package p_sales_pkg
as
type rc is ref cursor;
procedure get_query( p_cursor in out rc, p_start date, p_end date );
end;
/
create or replace package body p_sales_pkg
as
procedure get_query( p_cursor in out rc, p_start date, p_end date )
is
l_query long := 'select s_name ';
begin
for i in 1 .. trunc(p_end)-trunc(p_start)+1
loop
l_query := l_query || ', sum( decode( trunc(s_date), ' ||
'to_date( ''' || to_char(p_start+i-1,'yyyymmdd') ||
''', ''yyyymmdd'' ), s_qty, 0 )) "' ||
to_char(p_start+i-1) || '"';
end loop;
l_query := l_query || ' from sales group by s_name';
open p_cursor for l_query;
end;
end;
/
set autoprint on
var x refcursor
exec nw_demo_pkg.get_query( :x, '10-MAR-19', '13-MAR-19' );
This is really a very nice and challenging question. I disagree on the #APC point on SELECT part of a CREATE TABLE ... AS SELECT statement. Well we definitely can't do that. What i believe is to every problem in Oracle, there exists a solution.
You requirement can be achieved using a NESTED TABLE. See below:
Set Up:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
/
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
---Created an Object of Sales table to hold intermediate result
create or replace type sales_obj is OBJECT
(obj_sale_name varchar2(20),
obj_date_sal date,
obj_qty number(10)
);
/
-- Table of Sales Object.
create or replace type vtest1Tab is table of sales_obj;
/
Anonymous Block to Create table ccc:
DECLARE
VAR VTEST1TAB ;
vsql varchar2(500);
BEGIN
vsql := 'create table ccc(col1) NESTED TABLE COL1 STORE AS TAB1
as
Select cast(multiset(Select * from SALES) as VTEST1TAB )
from dual
';
Execute immediate vsql ;
END;
Output:
SQL> Select p.*
from ccc c,
table(c.COL1) p ;
In this link , The reply by "Zlatko Sirotic" covers exactly how to identify columns of the cursor and print them.
Look for "dyn_fetch", as the package is generic enough, it can work with any query for printing data. You can use the same approach to insert the data into a table that is created dynamically.

Update column value after some validation on it

I have a scenario where i need to update the value of a column or say append !
Below is test table and data
create table test (rsrc_nm varchar2(50), parm varchar2(400));
insert into test values ('HLRCamelProfileBasic','resource_code:7|resource_type:HLR_Camel_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:2|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:2|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:3|');
Here we have 2 columns of a staging table in which rsrc_nm and parm are given what i have to do is i need to update or append the rsrc_nm column for parm coulmn where the last value i.e. priority is changing from 1 to 2 for same rsrc_nm.
For example
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:1|
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:2|
Here we have same parm priority but the values are different and i need to insert these values into another table for which i need to separate all PIPE values so while i insert it in the table it gives me error for unique constraint because that table table have 3 columns
RSRC_NM PARM VAL
------------------------------- -------------- ----------------
HSSUSERProfileBasic Priority 1
HLRCamelProfileBasic Priority 1
HLRBaseProfileBasic Priority 2
And on this table we have primary key on first two columns which enforce unique constraint also so i can not insert " HLRCamelProfileBasic " rsrc_nm for parm " Priority " because it fails in uniqueness.
So i find a solution to overcome this if i can append or update the rsrc_nm HLRCamelProfileBasic to " HLRCamelProfileBasic_1 " for " Priority:1 " and HLRCamelProfileBasic_2 for " Priority:2 " and so on for all RSRC_NM in Staging table.
My solution --
declare
cursor c1 is select * from RSRC_SVC_MAPPING where parm_entry like '%Priorit%';
parm_entry_length number;
l_value varchar2(100);
parm_name varchar2(100);
parm_val varchar2(100);
l_c1 c1%rowtype;
l_cnt number := 0;
l_rsrc_nm varchar2(100);
begin
open c1;
loop
fetch c1 into l_c1;
parm_entry_length := length(l_c1.Parm_Entry) - length(replace(l_c1.Parm_Entry,'|',''));
for i in 1 .. parm_entry_length loop
select regexp_substr(l_c1.Parm_Entry,'[^|]+',1,i) into l_value from dual;
select regexp_substr (l_value, '[^:]+', 1, 1) into parm_name from dual;
select regexp_substr (l_value, '[^:]+', 1, 2) into parm_val from dual;
-- dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
for r in ( select count(*) cnt, resourcename
-- into l_cnt , l_rsrc_nm
from (select count(*), resourcename, parm_entry from RSRC_SVC_MAPPING where parm_entry like '%Priorit%' group by resourcename, parm_entry) group by resourcename)
loop
if r.cnt > 1
then
dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
update RSRC_SVC_MAPPING
set resourcename = r.resourcename||'_'||l_cnt
where resourcename = r.resourcename;
l_cnt := l_cnt +1;
end if;
end loop;
end loop;
exit when c1%notfound;
end loop;
exception
when others then
dbms_output.put_line('ERROR OCCURED '||SQLCODE||' '||sqlerrm);
dbms_output.put_line(dbms_utility.format_error_backtrace());
end;
Data which needs to be like after UPDATE is like
HSSUSERProfileBasic_1 resource_code:3|resource_type:HSS_User_Profile|Priority:1|
HSSUSERProfileBasic_2 resource_code:3|resource_type:HSS_User_Profile|Priority:2|
HLRBaseProfileBasic_2 resource_code:1|resource_type:HLR_Base_Profile|Priority:2|
HLRBaseProfileBasic_3 resource_code:1|resource_type:HLR_Base_Profile|Priority:3|
Here we have changed the value rsrc_nm column and append it with the value of priority i.e. 1 and 2
target table is like
create table target (rsrc_nm varchar2(100), parm varchar2(50), val varchar2(50) constraint tgt_pk primary key (rsrrc_nm, parm));
MY DB is -- Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

PL/SQL MERGE Using Collection

I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300