Rewrite Oracle PL/SQL procedure without merge - oracle

I need to rewrite the below procedure without merge.
So basically the insert/update needs to be done without merge, any other alternative if possible.
Database is Oracle12c/12.1
CREATE OR REPLACE PROCEDURE PUSH_DATA(p_id NUMBER) IS
BEGIN
MERGE INTO push_data_temp tgt
USING(
with rcte (id, data, lvl, result)
as (
select id, data, 1,
regexp_substr(data,
'("[^"]*"|[^, "]+)', 1, 1, null, 1) result
from disp_data where id=p_id
union all
select id, data, lvl + 1,
regexp_substr(data, '("[^"]*"|[^, "]+)', 1, lvl + 1, null, 1)
from rcte
where lvl <= regexp_count(data, '("[^"]*"|[^, "]+)')
)
select *
from (
select id, lvl, replace(result,'""','') as result
from rcte
)
pivot (max(result)
FOR (lvl) IN(1 AS col1
,2 AS col2
,3 AS col3
,4 AS col4
,5 AS col5
,6 AS col6
,7 AS col7
,8 AS col8
,9 AS col9
,10 AS col10
,11 AS col11
,12 AS col12))
) src
ON (src.id = tgt.id)
WHEN MATCHED THEN UPDATE
SET col1 = src.col1
, col2 = src.col2
, col3 = src.col3
, col4 = src.col4
, col5 = src.col5
, col6 = src.col6
, col7 = src.col7
, col8 = src.col8
, col9 = src.col9
, col10 = src.col10
, col11 = src.col11
, col12 = src.col12
WHEN NOT MATCHED THEN
INSERT (id, col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12)
VALUES (src.id, src.col1, src.col2,src.col3,src.col4,src.col5,
src.col6,src.col7,src.col8,src.col9,src.col10,src.col11,src.col12);
END;
/
#sujitmohanty had helped me with the procedure.

I have modified the procedure to use normal loop over merge. Keep in mind to change to merge later as it is the best option for insert/update operations as in your case.
I made the procedure parameter to optional, if you pass an pid it will do insert/update for the particular pid or else just pass null and it will do for all.
Note: For huge data you may need to rethink on using bulk collect operation to do the insert or update ( if only required)
CREATE OR REPLACE PROCEDURE PUSH_DATA(p_id NUMBER) IS
CURSOR cur_data IS
WITH rcte (id, data, lvl, result)
AS (
SELECT id, data, 1,
regexp_substr(data,
'("[^"]*"|[^, "]+)', 1, 1, null, 1) result
FROM disp_data where id=coalesce(p_id,id)
UNION ALL
SELECT id, data, lvl + 1,
regexp_substr(data, '("[^"]*"|[^, "]+)', 1, lvl + 1, null, 1)
FROM rcte
WHERE lvl <= regexp_count(data, '("[^"]*"|[^, "]+)')
)
SELECT *
FROM (SELECT id, lvl, replace(result,'""','') as result
FROM rcte)
PIVOT (MAX(result)
FOR (lvl) IN(1 AS col1
,2 AS col2
,3 AS col3
,4 AS col4
,5 AS col5
,6 AS col6
,7 AS col7
,8 AS col8
,9 AS col9
,10 AS col10
,11 AS col11
,12 AS col12));
var cur_data%ROWTYPE;
BEGIN
OPEN cur_data;
LOOP
FETCH cur_data INTO var;
EXIT WHEN cur_data%NOTFOUND;
UPDATE push_data_temp
SET col1 = var.col1
, col2 = var.col2
, col3 = var.col3
, col4 = var.col4
, col5 = var.col5
, col6 = var.col6
, col7 = var.col7
, col8 = var.col8
, col9 = var.col9
, col10 = var.col10
, col11 = var.col11
, col12 = var.col12
WHERE ID = var.id;
IF SQL%ROWCOUNT = 0
THEN
INSERT INTO push_data_temp(id, col1, col2, col3, col4, col5,
col6, col7, col8, col9, col10, col11, col12)
VALUES (var.id, var.col1, var.col2,var.col3,var.col4,var.col5,
var.col6,var.col7,var.col8,var.col9,var.col10,var.col11,var.col12);
END IF;
END LOOP;
END;
/
Test:-
BEGIN
push_data(p_id=>null);
END;
/
Finally Dbfiddle

Related

Splitting a row at a given column

I have a row with 12 columns. What I need to do for this application is create 2 rows, with the first 6 columns in first row, and the second 6 columns in the second row.
COL1 COL2 COL3 COL4 COL5 COL6 COL7 COL8 COL9 COL10 COL11 COL12
I need something like this:
COL1 COL2 COL3 COL4 COL5 COL6
COL7 COL8 COL9 COL10 COL11 COL12
Is this possible to achieve?
If the columns be all of the same type, then a union query might work:
SELECT COL1, COL2, COL3, COL4, COL5, COL6 FROM yourTable
UNION ALL
SELECT COL7, COL8, COL9, COL10, COL11, COL12 FROM yourTable;
Apart from tim's answer, you can also use connect by as following:
SELECT T.RN,
CASE WHEN LVL = 1 THEN COL1 ELSE COL7 END,
CASE WHEN LVL = 1 THEN COL2 ELSE COL8 END,
CASE WHEN LVL = 1 THEN COL3 ELSE COL9 END,
CASE WHEN LVL = 1 THEN COL4 ELSE COL10 END,
CASE WHEN LVL = 1 THEN COL5 ELSE COL11 END,
CASE WHEN LVL = 1 THEN COL6 ELSE COL12 END
FROM (SELECT ROWNUM RN, T.* FROM YOUR_TABLE T)
JOIN (SELECT LEVEL AS LVL
FROM DUAL CONNECT BY LEVEL <= 2) ON (1=1)
Cheers!!

How to get different table columns using the same Select statement

Hi have a table with 9 columns having the same datatype (all are percentage values). I'm trying to create one Select statement in a PL\SQL function to return tuple of 3, depending on an external parameter value.
Syntactically, something like this:
WITH tmp
AS (SELECT '1' col1,
'2' col2,
'3' col3,
'4' col4,
'5' col5,
'6' col6,
'7' col7,
'8' col8,
'9' col9
FROM DUAL
UNION
SELECT '10' col1,
'20' col2,
'30' col3,
'40' col4,
'50' col5,
'60' col6,
'70' col7,
'80' col8,
'90' col9
FROM DUAL
UNION
SELECT '100' col1,
'200' col2,
'300' col3,
'400' col4,
'500' col5,
'600' col6,
'700' col7,
'800' col8,
'900' col9
FROM DUAL)
SELECT CASE
WHEN externaparameter = 1 THEN (col1, col2, col3)
WHEN externaparameter = 2 THEN (col4, col5, col6)
WHEN externaparameter = 3 THEN (col7, col8, col9)
END
INTO var1, var2, var3
FROM tmp;
I have two solutions for this:
Implement CASE statements for each column. But it will create a large select statement and maybe confusing.
SELECT CASE
WHEN externaparameter = 1 THEN col1
WHEN externaparameter = 2 THEN col4
WHEN externaparameter = 3 THEN col7
END,
CASE
WHEN externaparameter = 1 THEN col2
WHEN externaparameter = 2 THEN col5
WHEN externaparameter = 3 THEN col8
END,
CASE
WHEN externaparameter = 1 THEN col3
WHEN externaparameter = 2 THEN col6
WHEN externaparameter = 3 THEN col9
END
INTO var1, var2, var3
FROM tmp;
Or, implement three select statement, with a union. But my original query have a couple of WHERE conditions and for that case I need to repeat them.
SELECT a, b, c
INTO var1, var2, var3
FROM (SELECT col1 a, col2 b, col3 c
FROM tmp
WHERE externalparameter = 1
UNION
SELECT col4 a, col5 b, col6 c
FROM tmp
WHERE externalparameter = 2
UNION
SELECT col7 a, col8 b, col9 c
FROM tmp
WHERE externalparameter = 3)
Did I have a more clean Select statement for this problem? And what are the advantages and drawbacks for each solutions?
If you can't selectively run one of three separate queries at run time based on externalparameter then option one using the case expressions is the cleaner solution.
Problems with the union solution:
Check the query plan generated to be sure, but I imagine that it will execute three separate select statements though they are mutually exclusive. (Unless 12c is much smarter than query optimizes I've word with to date.) This is because the plan might be reused so the plan has to include all three selects to have the correct one in all cases.
The union version includes an implicit distinct which either adds work if things are already distinct in the first version. Or changes the results versus the first version.
Dynamic SQL is sometimes used in cases like this, but I think three static statements run selectively would be better than building a dynamic SQL string.

Insert 1 row from 1 table and 2nd row form other table into a new table and so on

I have a table TABLE101 with the following fields:
COL1 COLB COLC COLD
ACT1 UYT 876 KJH
ACT2 CFG 976 TRY
I have another table TABLE102 as under:
COL1 COL2 COL3 COL4 COL5 COL6
ACt1 A1_B1 98 UI 2
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT
ACT2 A1_F1 9T UI 2
Now i want to insert data into a 3rd table which has all the fields from table101 and table 102 like:
COL1 COLB COLC COLD COL2 COL3 COL4 COL5 COL6 LVL
ACT1 UYT 876 KJH 1
ACt1 A1_B1 98 UI 2 2
ACT2 CFG 976 TRY 1
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT 2
ACT2 A1_F1 9T UI 2 2
So 1st i need to insert 1st row from table101 and for corresponding COL1 value i need to insert row from TABLE102.
In table101, COL1 is unique but in table 101 col1 can have multiple rows.
If i insert row from table101, i have to set lvl col to 1 and if i insert form table102 i set lvl col to 2
How can i do so?
You can use UNION ALL for combining your tables. Then use INSERT ... SELECT syntax for inserting into 3rd table.
SELECT COL1 , COLB, COLC , COLD, NULL AS COL2, NULL AS COL3, NULL AS COL4, NULL AS COL5, NULL AS COL6, 1 AS LVL FROM TABLE101
UNION ALL
SELECT COL1, NULL AS COLB, NULL AS COLC, NULL AS COLD, COL2, COL3, COL4 , COL5 , COL6 , 2 AS LVL FROM TABLE102
Setup:
create table table101 as
select 'ACT1' col1, 'UYT' colb, 876 colc, 'KJH' cold from dual union all
select 'ACT2' , 'CFG' , 976 , 'TRY' from dual
;
commit;
select * from table101;
COL1 COLB COLC COLD
---- ---- ---- ----
ACT1 UYT 876 KJH
ACT2 CFG 976 TRY
2 rows selected.
create table table102 as
select 'ACT1' col1, 'A1_B1' col2, '98' col3, 'UI' col4, null col5, '2' col6 from dual union all
select 'ACT2' , 'C1' , null , '00' , 'N' , null from dual union all
select 'ACT2' , 'D1_D4' , '1' , 'PP' , 'Y' , 'RT' from dual union all
select 'ACT2' , 'A1_F1' , '9T' , 'UI' , null , '2' from dual
;
commit;
select * from table102;
COL1 COL2 COL3 COL4 COL5 COL6
---- ----- ---- ---- ---- ----
ACT1 A1_B1 98 UI 2
ACT2 C1 00 N
ACT2 D1_D4 1 PP Y RT
ACT2 A1_F1 9T UI 2
4 rows selected.
create table table110 (col1 varchar2(4000), colb varchar2(4000), colc number, cold varchar2(4000),
col2 varchar2(4000), col3 varchar2(4000), col4 varchar2(4000), col5 varchar2(4000),
col6 varchar2(4000), lvl number);
Table TABLE110 created.
Insert statement and outcome:
insert into table110
select col1, colb, colc, cold, null, null, null, null, null, 1
from table101
union all
select col1, null, null, null, col2, col3, col4, col5, col6, 2
from table102
;
commit;
select * from table110;
COL1 COLB COLC COLD COL2 COL3 COL4 COL5 COL6 LVL
---- ---- ---- ---- ----- ---- ---- ---- ---- ---
ACT1 UYT 876 KJH 1
ACT2 CFG 976 TRY 1
ACT1 A1_B1 98 UI 2 2
ACT2 C1 00 N 2
ACT2 D1_D4 1 PP Y RT 2
ACT2 A1_F1 9T UI 2 2
6 rows selected.
select * from table110;

Find the maximum value in a column for each partition

I have table structure like:
CREATE TABLE new_test
( col1 NUMBER(2) NOT NULL,
col2 VARCHAR2(50) NOT NULL,
col3 VARCHAR2(50) NOT NULL,
col4 VARCHAR2(50) NOT NULL
);
It has data:
col1 col2 col3 col4
0 A B X
1 A B Y
2 A B Z
1 C D L
3 C D M
I need to find value of col4 which has maximum value for combination of col2 and col3. e.g. my result should be:
col4
Z
M
I tried to use oracle analytic function:
SELECT col4, MAX(col1) OVER (PARTITION BY col2, col3) FROM (
SELECT col2, col3, col4, col1
FROM new_test);
But it is not working as expected. Can you please help me to resolve this issue?
Update:
I could make it work using:
SELECT a.col4
FROM new_test a,
(SELECT col2,
col3,
col4,
MAX(col1) OVER (PARTITION BY col2, col3) AS col1
FROM new_test
) b
WHERE a.col2 = b.col2
AND a.col3 = b.col3
AND a.col4 = b.col4
AND a.col1 = b.col1;
Is there any better way than this?
If you expect that result:
col4
Z
M
You should write:
SELECT MAX(col4) AS col4 FROM new_test GROUP BY col2,col3
This will return the maximum value found in column col4 for each pair of col2,col3
If you need that:
COL2 COL3 COL4
A B Z
C D M
Write:
SELECT col2,col3,MAX(col4) AS col4 FROM new_test GROUP BY col2,col3
Finally, if you need:
COL1 COL2 COL3 COL4
2 A B Z
3 C D M
There are many variations. Like this one:
SELECT col1,col2,col3,col4
FROM new_test
NATURAL JOIN (SELECT col2,col3,MAX(col4) AS col4
FROM new_test GROUP BY col2,col3)

Query with a "With" Clause inside a For Loop Cursor of DB2 PL SQL

I have a very complex query that includes a "With" clause. This query works fine when executed on the DB2 Client. But if the same query is used inside a For Loop Cursor of a PL SQL stored procedure it does not work. On trying to apply the stored procedure to the database, it gives a syntax error as shown below.
SQL0104N An unexpected token "AS" was found following "col5 )
The for loop is as shown below.
FOR records AS cursors CURSOR FOR
(
WITH
temp1
(
col1, col2, col3, col4, col5
)
AS
(
SELECT
col1, col2, col3, col4, col5
FROM
table1
)
WITH
temp2
(
col6, col7, col8, col9, col10
)
AS
(
SELECT
col6, col7, col8, col9, col10
FROM
table2
)
SELECT col1, col2, col3, col4, col5, col6, co7, col8, col9, col10
FROM temp1, temp2
)
DO
-- Do Something here.
END FOR;
Can you please help solve this problem. Thanks in advance.
You have two problems. First, the FOR statement is incorrect; it should refer to a previously declared cursor:
...
CURSOR mycur IS WITH ... SELECT ...;
...
FOR rec IN mycur LOOP ...
Second,the query
WITH temp1 ( col1, col2, col3, col4, col5 ) AS (
SELECT col1, col2, col3, col4, col5 FROM table1
)
WITH temp2 ( col6, col7, col8, col9, col10 ) AS (
SELECT col6, col7, col8, col9, col10 FROM table2
is invalid and would never run by itself, so your claim that it executes in the CLP is untrue.
Finally I got this thing working. The solution was fairly simple. It was to remove the braces enclosing the query as shown below.
FOR records AS cursors CURSOR FOR
WITH
temp1
(
col1, col2, col3, col4, col5
)
AS
(
SELECT
col1, col2, col3, col4, col5
FROM
table1
)
WITH
temp2
(
col6, col7, col8, col9, col10
)
AS
(
SELECT
col6, col7, col8, col9, col10
FROM
table2
)
SELECT col1, col2, col3, col4, col5, col6, co7, col8, col9, col10
FROM temp1, temp2
DO
-- Do Something here.
END FOR ;
I am not really sure why it happens like this. It works just fine for any other normal query without a WITH clause in it.

Resources