ORACLE : How to use manual ID generated using MAX ID with Oracle Merge statement insert query - oracle

I have 2 tables like below,
Master_Data_1
Id Name
1 AAA
2 BBB
3 DDD
4 CCC
Master_Data_2
Id Name Upt_Flg
1 DDD False
2 EEE False
3 FFF False
both tables have same ID but different Name, and ID field is neither auto incremented nor sequence. But ID is a not null primary key column which will be generated based on MAX ID logic.
In this case i am trying to merge Master_Data_1 table with Master_Data_2 table using oracle merge statement.
So i am having issue in WHEN NOT MATCHED THEN case, i am trying to insert not matched records in to Master_data_2 table from master_data_1 table.
In this scenario ID in Master_Data_2 table will not be auto incremented, and also i cannot use the source table ID here because it will lead to unique constraint exception.
Note : I don't want to alter a table to make ID auto increment or adding sequence for ID.
Expected Output in Master_Data_2 table using merge query:
Id Name Upt_Flg
1 DDD TRUE
2 EEE False
3 FFF False
4 AAA False
5 BBB False
6 CCC False
is there any other way to achieve this?

I guess ROW_NUMBER analytic function might help you here.
MERGE INTO Master_Data_2 md2
USING (SELECT (SELECT MAX(id) FROM Master_Data_2) + ROW_NUMBER() OVER(ORDER BY id) new_id
,name
FROM Master_Data_1
) md1
ON (md2.name = md1.name)
WHEN MATCHED THEN UPDATE
SET Upt_flg = 'TRUE'
WHEN NOT MATCHED THEN
INSERT (md2.id, md2.name)
VALUES (Md1.new_id, md1.name);
Here is the fiddle

You can simply achieve it using INSERT and MINUS operator as following:
insert into master_data_2
select
(select max(id) from master_data_2) + row_number() over (order by 1),
name
from
(select name from master_data_1
minus
select name from master_data_2)
db<>fiddle demo
Cheers!!

Related

Insert all and validate duplicates in Oracle from DataFactory

I must execute multiple insert from azure data factory to Oracle and I am using the following statement
INSERT ALL INTO TABLENAME (CTCPIA,CTAN01,CTAN02,CTCRCD,CTCRR,CTAAN05,CTAAN04,CTCRCA,CTCRRB,CTDL011,CTDSG,CTCSIC,CTCPIL,CTEDDJ,CTUSER,CTPID,CTMKEY,CTUPMJ,CTTDAY) SELECT 'XXXX','31028775300.00','31028775300.00','COP','1','0.00','0.00','COP','0.00','Published','Executing','XXXX','XXXX','123059','LAKE-CHEC','XXX','XXX','122097','165729' FROM dual WHERE NOT EXISTS (SELECT 1 FROM TABLENAME WHERE CTCPIA='XXXX') SELECT * FROM dual;
I am adding the lines WHERE NOT EXISTS (SELECT 1 FROM TABLENAME WHERE CTCPIA='XXXX') to validate that no duplicates are inserted, however from datafactory it is throwing me the following error
How could I validate duplicates within the INSERT ALL statement? since I have to execute a lot of INSERT statements.
Thanks a lot.
I would suggest to refer documentation for INSERT ALL (Section: "The multitable insert statement looks like this")
I performed an insert to explain the scenario, see if that matches your use-case.
Current state of a table -
select * from test;
ID NAME
----- ----------
1 adsa
2 fa
2 xxx
Inserting 'ALL' to table TEST to avoid duplicates
based on condition (value NOT EXISTS)- No rows are inserted.
insert all
into test (id, name)
values (id+1, name||'a')
into test (id, name)
values (id+2, name||'b')
into test (id, name)
values (id+3, name||'c')
select 1 as id,'any name' as name from dual
where NOT EXISTS
(select 1 from test where name='xxx');
0 rows created.
Inserting 'ALL' to table TEST
based on condition (value EXISTS)-
insert all
into test (id, name)
values (id+1, name||'a')
into test (id, name)
values (id+2, name||'b')
into test (id, name)
values (id+3, name||'c')
select 1 as id,'any name' as name from dual
where EXISTS
(select 1 from test where name='xxx');
3 rows created.
select * from test;
ID NAME
----- ----------
1 adsa
2 fa
2 xxx
2 any namea
3 any nameb
4 any namec

Oracle Unique constraint on certain value

I have created a table (test_table), that has two columns (test_IP, test_STATUS). The table can have any number of IPs, however, only one IP with the status "Active" can be allowed. Any number of "Inactive" status' may be allowed. For reference (Active = 1, Inactive = 2)
For example:
test_IP test_STATUS
==============================
1.2.3.4 1
1.2.3.5 2
1.2.3.6 2
1.2.3.7 2
1.2.3.8 1 -- this should now fail (as there are multiple values containing 1 (Active))
Is it possible to have a unique contraint on a specific value? If so can someone please assist me on how I can achieve this? Thanks in advance!
Use a unique function based index, that considers only the active rows and resets the non active key to NULL
Example
select * from tab order by 1,2;
TEST_IP TEST_STATUS
------- -----------
1.1.1.1 1
1.1.1.1 2
1.1.1.1 2
1.1.1.1 2
1.1.1.2 1
create unique index tab_ixd on tab (case when test_STATUS = 1 then test_IP end);
insert into tab (test_IP,test_STATUS) values ('1.1.1.1', 2);
1 row inserted.
insert into tab (test_IP,test_STATUS) values ('1.1.1.1', 1);
ORA-00001: unique constraint (ZZZZ.TAB_IXD) violated
The above solution works if you allow one active row per IP address. If you need a constraint for only one active row per the whole table, use:
create unique index tab_ixd on tab (case when test_STATUS = 1 then test_STATUS end);
I would add virtual invisible column is_active:
alter table ip_list
add is_active varchar2(1)
invisible
generated always as
(case when test_STATUS=1 then 'y' end) virtual;
alter table ip_list add constraint uq_active_ip unique (is_active);
Since it's invisible, it will not affect existing queries. You can get it only if you specify is_active in your query.
Full example:
SQL> create table ip_list(test_IP,test_STATUS)
2 as
3 select '1.2.3.4', 1 from dual union all
4 select '1.2.3.5', 2 from dual union all
5 select '1.2.3.6', 2 from dual union all
6 select '1.2.3.7', 2 from dual ;
Table created.
SQL> alter table ip_list add is_active varchar2(1) invisible generated always as (case when test_STATUS=1 then 'y' end) virtual;
Table altered.
SQL> alter table ip_list add constraint uq_active_ip unique (is_active);
Table altered.
SQL> insert into ip_list(test_ip, test_status) values('1.2.3.8',1);
insert into ip_list(test_ip, test_status) values('1.2.3.8',1)
*
ERROR at line 1:
ORA-00001: unique constraint (XTENDER.UQ_ACTIVE_IP) violated
-- note that * does not return this new column:
SQL> select * from ip_list;
TEST_IP TEST_STATUS
------- -----------
1.2.3.4 1
1.2.3.5 2
1.2.3.6 2
1.2.3.7 2
-- but you can use it to filter active rows:
SQL> select * from ip_list where is_active='y';
TEST_IP TEST_STATUS
------- -----------
1.2.3.4 1
1 row selected.
If the test_status column can contain null value and the 1 value means that the ip is active, and instead of 2 you use null for the inactivity, then the unique constraint will work.
But I think it is not a really elegant solution.

Oracle Delete/Update in one query

I want to Delete the Duplicates from the table update the unique identifier and merge it with the already existing record.
I have a table which can contain following records -
ID Name Req_qty
1001 ABC-02/01+Time 10
1001 ABC-03/01+Time 20
1001 ABC 30
1002 XYZ 40
1003 DEF-02/01+Time 10
1003 DEF-02/01+Time 20
And I am expecting the records after the operation as follows:
ID Name Req_Qty
1001 ABC 60
1002 XYZ 40
1003 DEF 30
Any assistance would be really helpful. Thanks!
It is possible to do this in a single SQL statement:
merge into (select rowid as rid, x.* from test_table x ) o
using ( select id
, regexp_substr(name, '^[[:alpha:]]+') as name
, sum(reg_qty) as reg_qty
, min(rowid) as rid
from test_table
group by id
, regexp_substr(name, '^[[:alpha:]]+')
) n
on (o.id = n.id)
when matched then
update
set o.name = n.name
, o.reg_qty = n.reg_qty
delete where o.rid > n.rid;
Working example
This uses a couple of tricks:
the delete clause of a merge statement will only operate on data that has been updated, and so there's no restriction on what gets updated.
you can't select rowid from a "view" and so it's faked as rid before updating
by selecting the minimum rowid from per ID we make a random choice about which row we're going to keep. We can then delete all the rows that have a "greater" rowid. If you have a primary key or any other column you'd prefer to use as a discriminator just substitute that column for rowid (and ensure it's indexed if your table has any volume!)
Note that the regular expression differs from the other answer; it uses caret (^) to anchor the search for characters to the beginning of the string before looking for all alpha characters thereafter. This isn't required as the default start position for REGEXP_SUBSTR() is the first (1-indexed) but it makes it clearer what the intention is.
In your case, you will need to update the records first and then delete the records which are not required as following (Update):
UPDATE TABLE1 T
SET T.REQ_QTY = (
SELECT
SUM(TIN.REQ_QTY) AS REQ_QTY
FROM
TABLE1 TIN
WHERE TIN.ID = T.ID
)
WHERE (T.ROWID,1) IN
(SELECT TIN1.ROWID, ROW_NUMBER() OVER (PARTITION BY TIN1.ID)
FROM TABLE1 TIN1); --TAKING RANDOM RECORD FOR EACH ID
DELETE FROM TABLE1 T
WHERE NOT EXISTS (SELECT 1 FROM TABLE1 TIN
WHERE TIN.ID = T.ID AND TIN.REQ_QTY > T.REQ_QTY);
UPDATE TABLE1 SET NAME = regexp_substr(NAME,'[[:alpha:]]+');
--Update--
The following merge should work for you
MERGE INTO
(select rowid as rid, T.* from MY_TABLE1 T ) MT
USING
(
SELECT * FROM
(SELECT ID,
regexp_substr(NAME,'^[[:alpha:]]+') AS NAME_UPDATED,
SUM(Req_qty) OVER (PARTITION BY ID) AS Req_qty_SUM,
ROWID AS RID
FROM MY_TABLE1) MT1
WHERE RN = 1
) mt1
ON (MT.ID = MT1.ID)
WHEN MATCHED THEN
UPDATE SET MT.NAME = MT1.NAME_UPDATED, MT.Req_qty = MT1.Req_qty_SUM
delete where (MT.RID <> MT1.RID);
Cheers!!

Inserting selected data from table in one schema into another table in a different schema

I have a table A in Schema1 and table B in Schema2.
The tables have different columns.
Table A:
ID1 Name Code
-------------------------------
1 Skyler A0
2 Amanda A1
3 Rachel B0
4 Harvey C0
5 Louis B1
Table B:
ID Names Enterprise Modified_Date
------------------------------------------------------
1 Amanda 1 2018.08.10
2 Skyler 1 2018.08.11
As depicted, Schema1.A.Name = Schema2.B.Names
I want to insert the values "Rachel,Harvey and Louis" from A.Name into B.Names.
For b.ID, i have a sequence in place. Enterprise column is always 1 and modified date can e sysdate.
How can i achieve this in PL/SQL?
use insert Statement with select statement
insert into tabB (names,Enterprise,Modified_Date )
select Name,1,sysdate from tabA where Name in ('Rachel','Harvey','Louis');
You can do this by using below query.
insert into tableB (names,Enterprise,Modified_Date )
select Name,1,sysdate from tableA where Name not in (select distinct(Name) from tableB);

Concat values with group by in Oracle

My table Students is like
Id Name Dept
---------------
2 Ball A
3 Cat B
1 Dog C
2 Egg C
3 Fox D
4 Gun A
1 Hen B
1 Apple A
Now I want to get result as
Dept Names
-------------------
A Apple,Ball,Gun
B Hen,Cat
C Dog,Egg
D Fox
And here concatenation should be done based on the ascending order of Id column.
I have tried using wm_concat(), xmlagg(xmlelement()) functions but concatenation not follwing column Id order.
My Database version is Oracle 10.2.0.5.0
Thank You
Try ordering the values first on a subquery then using the wm_concat later. I've tested it on my workstation and it work fine. Couldn't create an sqlfiddle because it won't let me use the wm_concat function
select dept, wm_concat(name) from
(
select id, name, dept
from test
order by dept, id, name
) group by dept
Try with WM_CONCAT Function,
SELECT dept, WM_CONCAT(name) names
FROM(
SELECT dept, name
FROM students
order by name
)
GROUP BY dept
ORDER BY dept;

Resources