oracle sql dynamic insert statement - oracle

I have an oracle function that has a parameter which defines in which column the value should be inserted, e.g.
function something(p_value, p_nr)
is
...
begin
if p_nr = 1 then
insert into A (column1) (p_value);
else if p_nr = 2 then
insert into A (column2) (p_value);
...
end if;
I have a couple of values to enter in the table and only this value should be inserted dynamically. Is there an easier way to do this?

If your table structure defines default values for columns, you might also consider conditional insert:
insert all
when :p_nr = 1 then into A( col1 ) values( p_value )
when :p_nr = 2 then into A( col2 ) values( p_value )
when :p_nr = 3 then into A( col3 ) values( p_value )
select :p_value as p_value from dual
;
The advantage is that this query honors default values, look at an example:
create table A(
col1 varchar2( 10 ) default 'default 1',
col2 varchar2( 10 ) default 'default 2',
col3 varchar2( 10 ) default 'default 3'
);
variable p_nr number
variable p_value varchar2( 100 )
exec :p_nr:=2
exec :p_value:='value'
insert into A (col1, col2, col3)
values (case when :p_nr = 1 then :p_value end,
case when :p_nr = 2 then :p_value end,
case when :p_nr = 3 then :p_value end);
select * from A;
COL1 COL2 COL3
---------- ---------- ----------
value
and:
rollback;
insert all
when :p_nr = 1 then into A( col1 ) values( p_value )
when :p_nr = 2 then into A( col2 ) values( p_value )
when :p_nr = 3 then into A( col3 ) values( p_value )
select :p_value as p_value from dual
;
select * from A;
COL1 COL2 COL3
---------- ---------- ----------
default 1 value default 3

You could do:
insert into A (column1, column2)
values (case when p_nr = 1 then p_value end,
case when p_nr = 2 then p_value end);
That would put the value in one of the two columns, and null in the other; which way round depends on the flag value. (I've omitted the implied else null from both cases, but the intent might be clearer with it in).
Since that's now plain SQL it might not even need to be wrapped in a function (or procedure), depending what else you're doing.

Related

how can I insert Comma separated values in multiple column using merge into statement

I have a table and contains following fields in my Oracle DB
THEME_ID
DIR_ID
CLUSTER_ID
MONTH_ID
The Sample output should be like below, I am inserting as multiple row using below MERGE..INTO query. Total 12 rows needs to be inserted. As per my query it inserts only 6 because month_id not included as part of query
THEME_ID DIR_ID CLUSTER_ID MONTH_ID
23 2 a 1
23 2 c 1
23 2 v 1
23 2 a 2
23 2 c 2
23 2 v 2
123 2 a 1
123 2 c 1
123 2 v 1
123 2 a 2
123 2 c 2
123 2 v 2
I am inserting multiple comma separate values based on THEME_ID
for eg:
P_themeId = '23,123'
P_dirId = '2' ( only one value here always)
P_clusterId = 'a,c,v'
The below query works only for THEME_ID,DIR_ID and CLUSTER_ID. Now I want to insert MONTH_ID . This also contains multiple comma separated value as given in query. **(P_month varchar2(20) := '1,2';)** But I am not able to write a query for month part. is it possible to include this in MERGE INTO query or some other way
PROCEDURE SP_TEST (
P_themeId IN VARCHAR2,
P_dirId IN NUMBER,
P_clusterId IN VARCHAR2,
P_createdBy IN VARCHAR2,
P_ALL OUT SYS_REFCURSOR
) AS
**P_month varchar2(20) := '1,2';**
BEGIN
FOR i IN
(SELECT trim(regexp_substr(P_themeId, '[^,]+', 1, LEVEL)) l
FROM dual
CONNECT BY LEVEL <= regexp_count(P_themeId, ',')+1
)
LOOP
MERGE INTO TBL_TEST TA USING
(SELECT TMP1.CLUSTERID
FROM
(SELECT trim(regexp_substr(P_clusterId, '[^,]+', 1, LEVEL)) CLUSTERID
FROM dual
CONNECT BY LEVEL <= regexp_count(P_clusterId, ',')+1 ) TMP1
) TMP ON (TA.CLUSTER_ID = TMP.CLUSTERID AND TA.THEME_ID = i.l )
WHEN NOT MATCHED THEN
INSERT
( THEME_ID,DIR_ID, CLUSTER_ID,CREATED_BY
) VALUES
( i.l,P_dirId,TMP.CLUSTERID,P_createdBy
);
END LOOP;
OPEN P_ALL FOR SELECT
'INserted' AS msgs
FROM
dual;
END SP_TEST;
Rather than using a PL/SQL loop (or nested loops), you could do all the unnesting of the lists within the merge statement, using cross-joined subquery factoring (CTEs):
CREATE OR REPLACE PROCEDURE SP_TEST (
P_themeId IN VARCHAR2,
P_dirId IN NUMBER,
P_clusterId IN VARCHAR2,
P_createdBy IN VARCHAR2,
P_ALL OUT SYS_REFCURSOR
) AS
P_month varchar2(20) := '1,2';
BEGIN
MERGE INTO TBL_TEST ta
USING
(
WITH themes (theme_id) AS (
SELECT regexp_substr(P_themeId, '(.*?)(,|$)', 1, LEVEL, NULL, 1)
FROM dual
CONNECT BY LEVEL <= regexp_count(P_themeId, ',') + 1
),
clusters (cluster_id) AS (
SELECT regexp_substr(P_clusterId, '(.*?)(,|$)', 1, LEVEL, NULL, 1)
FROM dual
CONNECT BY LEVEL <= regexp_count(P_clusterId, ',') + 1
),
months (month_id) AS (
SELECT regexp_substr(P_month, '(.*?)(,|$)', 1, LEVEL, NULL, 1)
FROM dual
CONNECT BY LEVEL <= regexp_count(P_month, ',') + 1
)
SELECT t.theme_id, P_dirId as dir_id, c.cluster_id, m.month_id
FROM themes t
CROSS JOIN clusters c
CROSS JOIN months m
) tmp
ON (
ta.theme_id = tmp.theme_id
AND ta.dir_id = tmp.dir_id
AND ta.cluster_id = tmp.cluster_id
AND ta.month_id = tmp.month_id
)
WHEN NOT MATCHED THEN
INSERT (THEME_ID, DIR_ID, CLUSTER_ID, MONTH_ID, CREATED_BY)
VALUES (tmp.theme_id, tmp.dir_id, tmp.cluster_id, tmp.month_id, P_createdBy);
OPEN P_ALL FOR SELECT 'INserted' AS msgs FROM dual;
END SP_TEST;
/
db<>fiddle
or cross apply or cross--join lateral with inline views:
CREATE OR REPLACE PROCEDURE SP_TEST (
P_themeId IN VARCHAR2,
P_dirId IN NUMBER,
P_clusterId IN VARCHAR2,
P_createdBy IN VARCHAR2,
P_ALL OUT SYS_REFCURSOR
) AS
P_month varchar2(20) := '1,2';
BEGIN
MERGE INTO TBL_TEST ta
USING
(
SELECT t.theme_id, P_dirId as dir_id, c.cluster_id, m.month_id
FROM (
SELECT regexp_substr(P_themeId, '(.*?)(,|$)', 1, LEVEL, NULL, 1) AS theme_id
FROM dual
CONNECT BY LEVEL <= regexp_count(P_themeId, ',') + 1
) t
CROSS JOIN LATERAL (
SELECT regexp_substr(P_clusterId, '(.*?)(,|$)', 1, LEVEL, NULL, 1) AS cluster_id
FROM dual
CONNECT BY LEVEL <= regexp_count(P_clusterId, ',') + 1
) c
CROSS JOIN LATERAL (
SELECT regexp_substr(P_month, '(.*?)(,|$)', 1, LEVEL, NULL, 1) AS month_id
FROM dual
CONNECT BY LEVEL <= regexp_count(P_month, ',') + 1
) m
) tmp
ON (
ta.theme_id = tmp.theme_id
AND ta.dir_id = tmp.dir_id
AND ta.cluster_id = tmp.cluster_id
AND ta.month_id = tmp.month_id
)
WHEN NOT MATCHED THEN
INSERT (THEME_ID, DIR_ID, CLUSTER_ID, MONTH_ID, CREATED_BY)
VALUES (tmp.theme_id, tmp.dir_id, tmp.cluster_id, tmp.month_id, P_createdBy);
OPEN P_ALL FOR SELECT 'INserted' AS msgs FROM dual;
END SP_TEST;
/
db<>fiddle
Either way that generates 12 rows with your sample strings:
THEME_ID
DIR_ID
CLUSTER_ID
MONTH_ID
CREATED_BY
23
2
a
1
someone
23
2
a
2
someone
23
2
c
1
someone
23
2
c
2
someone
23
2
v
1
someone
23
2
v
2
someone
123
2
a
1
someone
123
2
a
2
someone
123
2
c
1
someone
123
2
c
2
someone
123
2
v
1
someone
123
2
v
2
someone
You don't really need PL/SQL, but it looks like you want to have a PL/SQL wrapper around the merge.

Oracle-How to making an effect of ignoring condition

MY_FUN fucntion returns as expected.
SELECT MY_FUN('e1','m1','L1') TEST1 FROM DUAL; --This return value 5 as expected.
TEST1
-----
5
Function parameters are, EQUIP,MODEL and LOT.
It select value in RR column in WC_BASE table
according to EQUIP, MODEL and ITEM1.
And the ITEM1 value is found at table M_IF by LOT.
But,How to modify MY_FUN to return value even ITEM1 value is NULL?
For instance, LOT 'L3' is NULL or LOT4 not exist in M_IF Table.
--How to modify MY_FUN to return 6?
SELECT MY_FUN('e1','m1','L3') TEST2 FROM DUAL;
SELECT MY_FUN('e1','m1','L4') TEST3 FROM DUAL;
Expected result is
TEST2
------
6
TEST3
------
6
[My Table and data]
CREATE TABLE WC_BASE (
EQUIP VARCHAR2(15),
MODEL VARCHAR2(15),
VAL VARCHAR2(15),
ITEM1 VARCHAR2(15),
RR VARCHAR2(15)
);
CREATE TABLE M_IF (
LOT VARCHAR2(15),
ITEM1 VARCHAR2(15)
);
INSERT INTO WC_BASE VALUES('e1','m1','2','c1','5');
INSERT INTO WC_BASE VALUES('e1','m1','1',NULL,'6');
INSERT INTO M_IF VALUES('L1','c1');
INSERT INTO M_IF VALUES('L2','c1');
INSERT INTO M_IF VALUES('L3',NULL);
[My Function]
CREATE OR REPLACE FUNCTION MY_FUN
(
P1 IN VARCHAR2,
P2 IN VARCHAR2,
P3 IN VARCHAR2
)
RETURN NUMBER AS V_VALUE NUMBER;
BEGIN
SELECT (
---
WITH SG AS (SELECT *
FROM WC_BASE
WHERE EQUIP =P1
AND MODEL =P2
AND ITEM1=(SELECT ITEM1 FROM M_IF WHERE LOT = P3) -- How to make an effect of ignoring this condition if the subquery returns null?
)
SELECT (x1) ANSWER
FROM ( SELECT
NVL(TO_NUMBER(RR),0) AS x1
FROM SG
WHERE VAL IN (SELECT TO_CHAR(MAX(TO_NUMBER(VAL))) FROM SG )
)
---
)
INTO V_VALUE
FROM DUAL ;
RETURN V_VALUE;
END MY_FUN;
/
You can change the WITH clause query using OUTER JOIN.
SELECT W.*
FROM WC_BASE W
LEFT JOIN M_IF M
ON (W.ITEM1 = M.ITEM1)
WHERE W.EQUIP =P1
AND W.MODEL =P2
AND M.LOT = P3
Cheers!!

Oracle-Using optional prameters in function?

I want to add at most three optional parameters to my Oracle function named TEST.
Returned value should be 2.
How to modify my function to make these queries work in a simplest way?
SELECT TEST('eq1','md1') TEST0 FROM DUAL; -- Shows 2 correctly
--How to make these queries also work?
SELECT TEST('eq1','md1','c1') TEST1 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1') TEST2 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1','e1') TEST3 FROM DUAL;
Table and Function are as below.
Table as below
CREATE TABLE T5 (
COL1 VARCHAR2(10),
COL2 VARCHAR2(10),
COL3 VARCHAR2(10),
COL4 VARCHAR2(10),
COL5 VARCHAR2(10),
VAL VARCHAR2(10)
);
INSERT INTO T5 VALUES ('eq1','md1','c1','d1','e1','2');
INSERT INTO T5 VALUES ('eq2','md2','c2','d2','e2','5');
INSERT INTO T5 VALUES ('eq3','md3','c3','d3','e3','3');
My funtion is,
CREATE OR REPLACE FUNCTION TEST
(p1 IN VARCHAR2,
p2 IN VARCHAR2
--How to add optional parameter p3?
--How to add optional parameter p4?
--How to add optional parameter p5?
)
RETURN NUMBER AS V_VALUE VARCHAR2(10);
BEGIN
SELECT(
SELECT VAL FROM T5
WHERE COL1 = p1
AND COL2 = p2
--How to add constraint COL3=p3?
--How to add constraint COL4=p4?
--How to add constraint COL5=p5?
)
INTO V_VALUE
FROM DUAL;
RETURN V_VALUE;
END;
/
You need to define the default value and that's it.
Your function code should look like as follows:
CREATE OR REPLACE FUNCTION TEST (
P1 IN VARCHAR2,
P2 IN VARCHAR2,
P3 IN VARCHAR2 DEFAULT NULL,
P4 IN VARCHAR2 DEFAULT NULL,
P5 IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER AS
V_VALUE VARCHAR2(10);
BEGIN
SELECT
(
SELECT
VAL
FROM
T5
WHERE
COL1 = P1
AND COL2 = P2
AND COL3 = COALESCE(P3, COL3)
AND COL4 = COALESCE(P4, COL4)
AND COL5 = COALESCE(P5, COL5)
)
INTO V_VALUE
FROM
DUAL;
RETURN V_VALUE;
END TEST;
/
Now, all the queries will run:
SELECT TEST('eq1','md1') TEST0 FROM DUAL;
SELECT TEST('eq1','md1','c1') TEST1 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1') TEST2 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1','e1') TEST3 FROM DUAL;
But, Please make sure that order of the passed parameter in the function is not broken.
You can not pass the P5 without passing P4 parameter value except the parameter names are used while calling the function.
Cheers!!

How to include column names along with rows dynamically in Oracle

I am trying to insert the column header and corresponding field into another table .
Table1 :
col1 col2 col3 col4
1 2 3 4
output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Table1 will be created dynamically , so structure of Table 1 might vary ,for eg .
sometime ,It might be
Case1:
col1 col2 col3 col4
1 2 3 4
For Case1 ,output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Or it might be
Case2:
col1 col2
1 2
For Case2 ,output should look like this:
COL_A COL_B COL_C COL_D
col1 1 col2 2
I got help with the static query for similar requirement in another question in Stack Overflow,but I was trying to convert the static query to dynamic query.
Static Query :
Static Table structure :
CREATE TABLE Table1
(col1 int, col2 int, col3 int, col4 int)
;
INSERT ALL
INTO Table1 (col1, col2, col3, col4)
VALUES (1, 2, 3, 4)
SELECT * FROM dual
;
Static Query :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
COL1
,COL2
,COL3
,COL4
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
'COL1' as VAL1
,'COL2' as VAL2
,'COL3' as VAL3
,'COL4' as VAL4
));
Output :
VAL1_C VAL1_V VAL2_C VAL2_V VAL3_C VAL3_V VAL4_C VAL4_V
COL1 1 COL2 2 COL3 3 COL4 4
As I tried to make the static query dynamic ,I tried this :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
select regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
select regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
));
But this throws error :
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 6 Column: 5
I tried using dynamic sql .Below is the dynamic sql I used :
declare
sql_stmt varchar2(4000);
pivot_clause1 varchar2(4000);
pivot_clause2 varchar2(4000);
begin
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
into pivot_clause1
FROM (select * from all_tab_columns where table_name = 'TEST11') ;
select * into pivot_clause2 from
(
select AA ||'''' aa
from (SELECT ''''||LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TEST11'))
);
dbms_output.put_line(pivot_clause1);
dbms_output.put_line(pivot_clause2);
sql_stmt := 'create table test13 as ('|| 'SELECT * FROM
(
SELECT *
FROM TEST11
UNPIVOT(val FOR col IN ( '
|| pivot_clause1 || '
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (' ||
pivot_clause2 || '
))
)';
execute immediate sql_stmt ;
end;
/
It is working fine for a table having description as below :
Name Null Type
---- ---- ----------
COL1 NUMBER(38)
COL2 NUMBER(38)
COL3 NUMBER(38)
COL4 NUMBER(38)
But it is not working for a table having description :
COL1 DATE
COL1 VARCHAR2(100)
COL1 VARCHAR2(100)
COL1 NUMBER
COL1 NUMBER
I am getting below error :
Error report -
ORA-01790: expression must have same datatype as corresponding expression
ORA-06512: at line 38
01790. 00000 - "expression must have same datatype as corresponding expression"
*Cause:
*Action:
I just learned unpivot cannot be used with columns having different datatype,Is there any way around ?
Can someone please help !!

Oracle - Cannot update table record using bind variable

This query returns 1 row:
SELECT col1, col2 FROM table1 WHERE col1 = :column1;
But this updates 0 rows:
UPDATE table1 SET col2 = :column2 WHERE col1 = :column1;
COMMIT;
I added this constraint to set col1 as primary key, but it didn't fix it.
ALTER TABLE table1 ADD CONSTRAINT col1_pk PRIMARY KEY (col1);
I am trying this from SQL Developer, any idea why it does not update the row?
EDIT:
col1 is VARCHAR2(32 BYTE) NOT NULL
col2 is CLOB NOT NULL
EDIT 2: Test Case, set :var1 to 0011223344556677 in the select and update sentences.
CREATE TABLE MY_TABLE
( COL1 VARCHAR2(32 BYTE) NOT NULL ENABLE,
COL2 CLOB,
CONSTRAINT "MY_TABLE_PK" PRIMARY KEY ("COL1")
)
INSERT INTO MY_TABLE (COL1, COL2) VALUES ('0011223344556677', '1434407992143440799214344079921434407992');
SELECT * FROM MY_TABLE WHERE COL1 = :var1;
UPDATE MY_TABLE SET COL2 = 'test' WHERE COL1 = :var1;
COMMIT;
TL;DR - Make sure the value being stored in the bind variable is parsed as a character string not a number.
I've run this in SQL Developer (Version 4.0.3.16):
CREATE TABLE MY_TABLE
( COL1 VARCHAR2(32 BYTE) NOT NULL ENABLE,
COL2 CLOB,
CONSTRAINT "MY_TABLE_PK" PRIMARY KEY ("COL1")
);
/
INSERT INTO MY_TABLE (COL1, COL2) VALUES ('0011223344556677', '1434407992143440799214344079921434407992');
/
VARIABLE var1 VARCHAR2(32);
/
BEGIN
:var1 := '0011223344556677';
END;
/
SELECT * FROM MY_TABLE WHERE COL1 = :var1;
/
UPDATE MY_TABLE SET COL2 = 'test' WHERE COL1 = :var1;
/
COMMIT;
/
SELECT * FROM MY_TABLE;
/
And it runs fine:
table MY_TABLE created.
1 rows inserted.
anonymous block completed
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 1434407992143440799214344079921434407992
1 rows updated.
committed.
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 test
If you change the variable assignment to (remove quotes):
BEGIN
:var1 := 0011223344556677;
END;
Then the value is parsed as a number and the leading zeros are ignored and the output is:
table MY_TABLE created.
1 rows inserted.
anonymous block completed
no rows selected
0 rows updated.
committed.
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 1434407992143440799214344079921434407992

Resources