update and insert into table in with single query - sql

table 1
id pk1 timestamp
1 a 10-jul-2019
2 h 11-mar-2019
3 k 19-jul-2019
4 j 7-n0v-2018
5 h 11-jul-2019
table 2
col start_date end_date
a 10-jul-2019
h 11-mar-2019 11-jul-2019
k 19-jul-2019
j 7-nov-2018
h 11-jul-2019
Q:> I want this process to be repeat after equal interval of time.
if a new value got enter into the table 1 then same value should enter into the table 2 but if existing values enters in table 1 then just update end date of previous same value of table 2 and add one new value with null end date into table 2 (example value H in table 1 and table 2).
we need to use only single query.
with merge we are not able to get this

if a new value got enter into the table 1 then same value should enter
into the table 2 but if existing values enters in table 1 then just
update end date of previous same value of table 2 and add one new
value with null end date into table 2
Your scenario need a Trigger to be created on Table1. You can put down your logic to update Table2 in the Trigger.
See below demo:
--Table1 DDL
Create table tab1 (
id number,
pk1 varchar(1),
time_stamp date
);
--Table2 DDL
create table tab2 (
col varchar(1),
start_date date,
end_date date
);
Here is Trigger on Table1
Create or replace trigger t1
before insert on tab1
for each row
begin
DECLARE
l_exists INTEGER;
BEGIN
SELECT COUNT(*)
INTO l_exists
FROM tab2
WHERE col = :new.pk1 ;
IF l_exists = 0
THEN
INSERT INTO TAB2
values
(:new.pk1,:new.time_stamp,null);
ELSE
Update tab2
set end_date = :new.time_stamp
where col = :new.pk1;
INSERT INTO TAB2
values
(:new.pk1,:new.time_stamp,null);
END IF;
END;
end;
--Execution:
insert into tab1 values (1,'a',to_date('10-jul-2019','DD-MON-YYYY'));
insert into tab1 values (2,'h',to_date('11-mar-2019','DD-MON-YYYY'));
insert into tab1 values (3,'k',to_date('19-jul-2019','DD-MON-YYYY'));
insert into tab1 values (4,'j',to_date('07-nov-2019','DD-MON-YYYY'));
insert into tab1 values (5,'h',to_date('11-jul-2019','DD-MON-YYYY'));
Commit;
SQL> select * from tab1;
ID P TIME_STAM
---------- - ---------
1 a 10-JUL-19
3 k 19-JUL-19
4 j 07-NOV-19
2 h 11-MAR-19
5 h 11-JUL-19
SQL> select * from tab2;
C START_DAT END_DATE
- --------- ---------
a 10-JUL-19
k 19-JUL-19
j 07-NOV-19
h 11-MAR-19 11-JUL-19
h 11-JUL-19

Related

PL/SQL insert data into a column with values corresponding to values of another column

There is a table with table structure something like this customer_id number(10), listing_id number(12).
Now The data in this table is somewhat above 10 million so i've been given a task of adding a process_id to the table so that the data can be processed in batches in future operations.
so I added a column process_id to the table
alter table temp_lid_cid add process_id number(1) ;
Now i have to add process ids to the customer_ids at random 1 2 3 4 5 6 7, so that they will get processed according to their process_ids when condition where process_id = $1
There are millions of data so i wrote a simple PL
declare
i temp_lid_cid.customer_id%type;
c temp_lid_cid.process_id%type;
begin
c:=0;
for i in (select customer_id from temp_lid_cid)
loop
if (c = 7) then
c := 0;
end if;
c := c+1;
execute immediate q'[insert into temp_lid_cid(process_id) select :var1 as process_id from temp_lid_cid where customer_id = :var2]'using i,c;
end loop;
end;
It throws this error
Error report -
ORA-06550: line 12, column 145:
PLS-00457: expressions have to be of SQL types
ORA-06550: line 12, column 9:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I tried running the insert statement without execute immediate too but it still threw an error.I also tried running the insert statement for a single customer outside the PL and it worked fine.
If you can suggest any other way to do what i'm trying to do without PL that would be great too.
Now The data in this table is somewhat above 10 million so i've been
given a task of adding a process_id to the table so that the data can
be processed in batches
Insert will Insert a new row to your table. You need an Update statement to fullfill your requirement. See below:
DECLARE
i temp_lid_cid.customer_id%TYPE;
c temp_lid_cid.process_id%TYPE;
BEGIN
c := 0;
FOR i IN (SELECT customer_id FROM temp_lid_cid )
LOOP
IF ( c = 7 )
THEN
c := 0;
END IF;
c := c + 1;
UPDATE temp_lid_cid
SET
process_id = c
WHERE customer_id = i.customer_id;
END LOOP;
COMMIT;
END;
Error in your code:
In your Loop, customer_id is fetched using i.customer_id. So in your insert statement replace as below:
using c,i.customer_id;
Suggestion:
Since the number of rows is 10 Million so i would recommend to use BULK Operation to perform Update.
DECLARE
c temp_lid_cid.process_id%TYPE;
type v_cust_id is table of temp_lid_cid.customer_id%TYPE index by pls_integer;
i v_cust_id;
BEGIN
c := 0;
SELECT customer_id
BULK COLLECT INTO i
FROM temp_lid_cid;
FORALL rec IN 1..i.count
UPDATE temp_lid_cid
SET
process_id = c + i(rec) -- Updating Process_Id with Customer_id
WHERE customer_id = i(rec);
COMMIT;
END;
you interchanged your sql type.
declare
i temp_lid_cid.process_id%type;
c temp_lid_cid.customer_id%type;
Why use PL/SQL block and loop for it. It can be done using a single merge statement as following: (I am using the range from 1-4 numbers, you can use 1-7 numbers by replacing 4 with 7 in merge statement)
Oracle table creation:
SQL> CREATE TABLE TEMP_LID_CID (
2 CUSTOMER_ID NUMBER(10),
3 LISTING_ID NUMBER(12),
4 PROCESS_ID NUMBER(1)
5 );
Table created.
Inserting data into the table:
SQL> insert into temp_lid_cid values (1,10,null);
1 row created.
SQL> insert into temp_lid_cid values (1,20,null);
1 row created.
SQL> insert into temp_lid_cid values (1,30,null);
1 row created.
SQL> insert into temp_lid_cid values (1,40,null);
1 row created.
SQL> insert into temp_lid_cid values (1,50,null);
1 row created.
SQL> insert into temp_lid_cid values (2,10,null);
1 row created.
SQL> insert into temp_lid_cid values (2,20,null);
1 row created.
SQL> insert into temp_lid_cid values (2,30,null);
1 row created.
Current view of the data
SQL> select * from TEMP_LID_CID;
CUSTOMER_ID LISTING_ID PROCESS_ID
----------- ---------- ----------
1 10
1 20
1 30
1 40
1 50
2 10
2 20
2 30
8 rows selected.
SQL>
query to achieve the desired result:
SQL> MERGE INTO TEMP_LID_CID T USING (
2 SELECT
3 T1.*,
4 T1.ROWID AS RID,
5 MOD(ROW_NUMBER() OVER(
6 ORDER BY
7 T1.CUSTOMER_ID
8 ), 4) AS RANDOM_PROCESS_ID -- replace 4 with 7
9 FROM
10 TEMP_LID_CID T1
11 )
12 T1 ON ( T.ROWID = T1.RID )
13 WHEN MATCHED THEN UPDATE SET T.PROCESS_ID = DECODE(T1.RANDOM_PROCESS_ID, 0, 4, T1.RANDOM_PROCESS_ID); -- replace 4 with 7
Data after update:
SQL> select * from TEMP_LID_CID;
CUSTOMER_ID LISTING_ID PROCESS_ID
----------- ---------- ----------
1 10 1
1 20 2
1 30 3
1 40 4
1 50 1
2 10 2
2 20 3
2 30 4
8 rows selected.
SQL>
Since process_id can be random, you could also use a simple query like this:
update temp_lid_cid set process_id = mod(rownum,7)+1;

Determine if row has ever been updated

Might be a long shot, but I'm looking for a way to see if a row has ever been updated since it was inserted.
Ex.
CREATE TABLE TEST_DATA AS
( SELECT 'A' AS dummy
FROM dual
UNION
SELECT 'B' AS dummy
FROM dual
);
UPDATE TEST_DATA
SET dummy = 'C'
WHERE dummy = 'B';
Is there any way I can tell that the 'C' record has been updated?
Also, I have absolutely no control over the data model so I can't add an add timestamp and an update timestamp to the actual table.
this will work:
create table test_data(a varchar(1));
insert into test_data values('A');
insert into test_data values('B');
insert into test_data values('C');
insert into test_data values('D');
select * from test_data;
A
B
C
D
create table noofchanges(data varchar(1),numberofchanges int) ;
insert into noofchanges(data,numberofchanges) select a,0 from test_data;
select * from noofchanges;
A 0
B 0
C 0
D 0
CREATE OR REPLACE TRIGGER test_data_before_update
BEFORE UPDATE
ON test_data
FOR EACH ROW
BEGIN
update noofchanges
set numberofchanges=numberofchanges+1
where data=:old.a;
END;
update test_data set a='A' where a='B';
select * from test_data;
A
A
C
D
select * from noofchanges
A 0
B 1
C 0
D 0
thank you!!!!!!

How to validate data after update in oracle

I have two tables A and B which are not related.
SQL> select * from A;
OLD_ID R_ID
---------- ----------
TA-BC 1
TB-BC 2
TC-BC 3
TD-BC 4
TE-BC 5
TF-BC 6
TG-BC 7
8
SQL> select * from B;
NEW_ID OLD_ID S_CD
---------- ---------- -----
1 TA-BC A
2 TB-BC B
3 TC-BC C
4 TD-BC A
5 TE-BC B
6 TF-BC F
7 TG-BC C
8 TH-BC B
I need to update column "old_id" in table A with corresponding "new_id" values from table B where A.OLD_ID = B.OLD_ID.
I have written something like below. The data in table A and B has around 1 million records the one i gave above here is sample data. Since the data volume is high am updating for every 25k records and commiting it in a loop.
DECLARE
v_cnt number := 1;
BEGIN
WHILE v_cnt > 0 LOOP
UPDATE /*+ parallel(A 10) */ A a
SET a.old_id =
(SELECT DISTINCT new_id
FROM B b
WHERE b.old_id = a.old_id)
WHERE EXISTS
(SELECT 1
FROM B b1
WHERE b1.old_id = a.old_id and ROWNUM < 25000;
v_cnt := SQL%ROWCOUNT;
COMMIT;
END LOOP;
END;
/
I would like to know how can i print how many records got updated and how can i validate whether all the records in table A which has the matching record in table B with old_id has got updated correctly or not. What is the query i can write before/after the update statement to validate if table A "old_id" column has been updated correctly with values from table B "new_id" columns
Below is the table creation script.
create table A(old_id varchar2(10),r_id number);
insert into A values ('TA-BC',1);
insert into A values ('TB-BC',2);
insert into A values ('TC-BC',3);
insert into A values ('TD-BC',4);
insert into A values ('TE-BC',5);
insert into A values ('TF-BC',6);
insert into A values ('TG-BC',7);
insert into A(r_id) values(8);
commit;
create table B(new_id number,old_id varchar2(10),s_cd varchar2(5));
insert into B values (1,'TA-BC','A');
insert into B values (2,'TB-BC','B');
insert into B values (3,'TC-BC','C');
insert into B values (4,'TD-BC','A');
insert into B values (5,'TE-BC','B');
insert into B values (6,'TF-BC','F');
insert into B values (7,'TG-BC','C');
insert into B values (8,'TH-BC','B');
commit;
I don't see why you are replacing OLD_ID with NEW_ID when they are different data types: OLD_ID is char and NEW_ID is an integer.
It would be better to add a new field (column) to the table to store NEW_ID and update that.
You can then check the mapping of old to new has been performed correctly and take advantage of the fact the new id is the correct data type for joins to other tables using NEW_ID

How to update only one record of duplicates

I want to update status to inactive ( Status=ā€™Iā€™) for all duplicate record except one in sql, default status is active (Status=ā€™Aā€™ ) for all records in table. It should be done without using any inbuilt sql function ex: row_num(), rank(), set rowcount etc.
CREATE TABLE dup_test
(
Emp_ID INT,
Mgr_ID INT,
Status Varchar(5)
)
INSERT INTO dup_test VALUES (1,1,'A');
INSERT INTO dup_test VALUES (1,1,'A');
INSERT INTO dup_test VALUES (1,1,'A');
INSERT INTO dup_test VALUES (2,2,'A');
INSERT INTO dup_test VALUES (2,2,'A');
INSERT INTO dup_test VALUES (3,3,'A');
Expected Result:
Emp_ID, Mgr_ID, Status
1 1 A
1 1 I
1 1 I
2 2 A
2 2 I
3 3 A
Thanks in advance.
Alter the table and add an identity column (ID):
ALTER TABLE dup_test
ADD id INT NOT NULL IDENTITY (1, 1)
Then something like the following will work:
UPDATE dup_test SET
Status='I'
FROM dup_test dt LEFT OUTER JOIN
(SELECT Emp_ID, MAX(ID) AS maxid
FROM dup_test
GROUP BY Emp_ID) AS dt2 ON dt.Emp_ID=dt2.Emp_ID AND dt.ID=dt2.maxid
WHERE dt2.maxID IS NULL

Query to output values which are not present in table

Need help in Oracle query
Requirement:
I have 5 rows in a table lets say ID = 1, 2, 3, 4, 5
Requirement is as such that user may pass any value as input and if that value is not there in table then query should return me the value which is not present.
Ex:
1. If user passes 9 then Oracle query should return the output as 9
2. If user passes 1,2,10 then Oracle query should return the output as 10
as 9 and 10 in above example are not in table.
I am using following query but not getting result.
SELECT ID
FROM TABLE_NAME WHERE ID NOT IN
(SELECT ID
FROM TABLE_NAME where ID NOT in (1,2,10))
create table z (id number);
Table created.
SQL> insert into z values (1);
1 row created.
SQL> insert into z values (2);
1 row created.
SQL> insert into z values (3);
1 row created.
SQL> insert into z values (4);
1 row created.
SQL> insert into z values (5);
1 row created.
SQL> select 10 id from dual
2 minus
3 select id from z;
ID
----------
10
You could use a nested table as input:
SQL> CREATE TABLE table_name (ID NUMBER NOT NULL);
Table created
SQL> INSERT INTO table_name (SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 5);
5 rows inserted
SQL> CREATE TYPE tab_number AS TABLE OF NUMBER;
2 /
Type created
SQL> SELECT *
2 FROM TABLE(tab_number(1,2,10)) x
3 WHERE x.column_value NOT IN (SELECT ID FROM table_name);
COLUMN_VALUE
------------
10