Update parent child table blocks with pk generation from sequence - oracle

CREATE TABLE t1 (
t1pk NUMBER PRIMARY KEY NOT NULL
,t1val NUMBER
);
CREATE TABLE t2 (
t2pk NUMBER PRIMARY KEY NOT NULL
,t2fk NUMBER
,t2val NUMBER
,CONSTRAINT t2fk FOREIGN KEY (t2fk)
REFERENCES t1 (t1pk) ON DELETE CASCADE
);
INSERT INTO t1 (t1pk, t1val)
VALUES (1, 1);
INSERT INTO t2 (t2pk, t2fk, t2val)
VALUES (1, 1, 1);
COMMIT;
CREATE SEQUENCE seq1
MINVALUE 1
MAXVALUE 999999999999999999999999999;
CREATE OR REPLACE TRIGGER trg1
BEFORE
INSERT -- Problematic code.
OR UPDATE
OR DELETE ON t1
FOR EACH ROW
BEGIN
IF (INSERTING)
THEN
-- Problematic code.
:NEW.t1pk := seq1.NEXTVAL;
END IF;
END trg1;
/
Session 1:
UPDATE t2 -- Table 2!
SET t2val = t2val;
Session 2:
UPDATE t1 -- Table 1!
SET t1val = t1val;
In session 2 the update does not come back and waits until session 1 closes the transaction with commit or rollback. This is not what I do expect. The reason seems to be the trigger code with pk generation from sequence. If I remove that sequence code the update in session 2 does not wait and come back while the transaction of session 1 is still open. What is wrong?
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

Related

oracle deadlock in one update statement?

First my oracle version:
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
I create table and insert two rows:
create table test_table
(
objectId VARCHAR2(40) not null,
dependId VARCHAR2(40) not null
);
insert into test_table values(1, 10000);
insert into test_table values(2, 20000);
commit;
Then open two sessions and execute the following commands in order.
Case1:
session1:
update test_table set dependId=100000 where objectid in (2);
session2:
update test_table set dependId=200000 where objectid in (1,2);
seesion1:
update test_table set dependId=100000 where objectid in (1);
and session2 showsORA-00060: deadlock detected while waiting for resource
Case2
session1:
update test_table set dependId=100000 where objectid in (1);
session2:
update test_table set dependId=200000 where objectid in (2,1);
seesion1:
update test_table set dependId=100000 where objectid in (2);
and no deadlock occur.
Please explain the reason. How the update ... where objectid in (1,2) hold the lock?
This comes down to the order the database tries to acquire locks on the rows.
In your example objectid = 1 is "first" in the table. You can verify this by sorting the data by rowid:
create table test_table
(
objectId VARCHAR2(40) not null,
dependId VARCHAR2(40) not null
);
insert into test_table values(1, 99);
insert into test_table values(2, 0);
commit;
select rowid, t.* from test_table t
order by rowid;
ROWID OBJECTID DEPENDID
AAAT9kAAMAAAdMVAAA 1 99
AAAT9kAAMAAAdMVAAB 2 0
If in session 1 you now run:
update test_table set dependId=100000 where objectid in (2);
You're updating the "second" row in the table. When session 2 runs:
update test_table set dependId=200000 where objectid in (2,1);
It reads the data block. Then tries to acquire locks on them in the order they're stored. So it looks at the first row (objectid = 1), asks "is the locked?" Finds the answer is no. And locks the row.
It then repeats this process for the second row. Which is locked by session 1. When querying v$lock, you should see two entries requesting 'TX' locks in lmode = 6. One for each session:
select sid from v$lock
where type = 'TX'
and lmode = 6;
SID
75
60
So at this stage both sessions have one row locked. And session 2 is waiting for session 1.
In session 1 you now run:
update test_table set dependId=100000 where objectid in (1);
BOOOOM! Deadlock!
OK, but how can we be sure that this is due to the order rows are stored?
Using attribute clustering (a 12c feature), we can change the order rows are stored in the blocks, so objectid = 2 is "first":
alter table test_table
add clustering
by linear order ( dependId );
alter table test_table move;
select rowid, t.* from test_table t
order by rowid;
ROWID OBJECTID DEPENDID
AAAT9lAAMAAAdM7AAA 2 0
AAAT9lAAMAAAdM7AAB 1 99
Repeat the test. In session 1:
update test_table set dependId=100000 where objectid in (2);
So this has locked the "first" row. In session 2:
update test_table set dependId=200000 where objectid in (2,1);
This tries to lock the "first" row. But can't because session 1 has it locked. So at this point only session 1 holds any locks.
Check v$lock to be sure:
select sid from v$lock
where type = 'TX'
and lmode = 6;
SID
60
And, sure enough, when you run the second update in session 1 it completes:
update test_table set dependId=100000 where objectid in (1);
NOTE
This doesn't mean that update is guaranteed to lock rows in the order they're stored in the table blocks. Adding or removing indexes could affect this behaviour. As could changes between Oracle Database versions.
The key point is that update has to lock rows in some order. It can't instantly acquire locks on all the rows it'll change.
So if you have two or more sessions with multiple updates, deadlock is possible. So you should start your transaction by locking all the rows you intend to change with select ... for update.

Unique value generation for primary key for multiple tables

I do have 3 tables. I want to generate ID for each below table and that should be unique across each table.
Table1 ID - primary
ID
1 -> 2 -> 3
Table2 ID - primary
ID
4 -> 5
Table3 ID - primary
ID
6 -> 7 -> 8
Whenever a new entry is made to the above tables it should generate unique values across the tables
For next time when I want to insert 2 records to table 1 it should be
Table1 ID - primary
ID
9 -> 10.
Do we can create a trigger to accomplish this in Oracle
Use a trigger on each table and create a Sequence
CREATE SEQUENCE seq
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
Then on the trigger on insert do something like:
select seq.nextval
into :new.id
from dual;
This was asked before. Look here
CREATE OR REPLACE TRIGGER my_trigger
BEFORE INSERT
ON qname
FOR EACH ROW
-- Optionally restrict this trigger to fire only when really needed
WHEN (new.qname_id is null)
DECLARE
v_id qname.qname_id%TYPE;
BEGIN
-- Select a new value from the sequence into a local variable. As David
-- commented, this step is optional. You can directly select into :new.qname_id
SELECT qname_id_seq.nextval INTO v_id FROM DUAL;
-- :new references the record that you are about to insert into qname. Hence,
-- you can overwrite the value of :new.qname_id (qname.qname_id) with the value
-- obtained from your sequence, before inserting
:new.qname_id := v_id;
END my_trigger;

Difference between Oracle sequence last number and max id inserted

I'm working on a application used by multiple users. They can insert, delete and update rows in the database which fires triggers what write into log tables. The problem is that the difference between the maximum id inserted in the log table and the last number generated by the sequences used by the trigger is changing continously. Sometimes an primary key exception is generated because the the sequence generate values that are already inserted in the table.
CREATE OR REPLACE
TRIGGER EVALUACION.TCALIF_DEL
AFTER DELETE ON EVALUACION.CALIFICACIONES FOR EACH ROW
BEGIN
INSERT INTO LOG_CALIFICACIONES (ID_BITACORA, ID_EVALUACION, ANIO, CALIFICACION, OBSERVACION, USUARIO, FECHA_HORA, TIPO_OPERACION_BD)
SELECT SEQ_CALIF.NEXTVAL, :OLD.ID_EVALUACION, :OLD.ANIO, :OLD.CALIFICACION, :OLD.OBSERVACION, :OLD.USUARIO, :OLD.FECHA_HORA, 'D' FROM DUAL;
END;
CREATE OR REPLACE
TRIGGER EVALUACION.TCALIF_INS
AFTER INSERT ON EVALUACION.CALIFICACIONES FOR EACH ROW
BEGIN
INSERT INTO LOG_CALIFICACIONES (ID_BITACORA, ID_EVALUACION, ANIO, CALIFICACION, OBSERVACION, USUARIO, FECHA_HORA, TIPO_OPERACION_BD)
SELECT SEQ_CALIF.NEXTVAL, :OLD.ID_EVALUACION, :OLD.ANIO, :OLD.CALIFICACION,:OLD.OBSERVACION, :OLD.USUARIO, :OLD.FECHA_HORA, 'I' FROM DUAL;
END;
CREATE OR REPLACE
TRIGGER EVALUACION2010.TCALIF_UPD
AFTER UPDATE ON EVALUACION.CALIFICACIONES FOR EACH ROW
BEGIN
INSERT INTO LOG_CALIFICACIONES (ID_BITACORA, ID_EVALUACION, ANIO, CALIFICACION, OBSERVACION, USUARIO, FECHA_HORA, TIPO_OPERACION_BD)
SELECT SEQ_CALIF.NEXTVAL, :OLD.ID_EVALUACION, :OLD.ANIO, :OLD.CALIFICACION, :OLD.OBSERVACION, :OLD.USUARIO, :OLD.FECHA_HORA, 'U' FROM DUAL; END;
The code for the sequence is:
CREATE SEQUENCE "EVALUACION"."SEQ_CALIF"
MINVALUE 1
MAXVALUE 999999999999999999999999999
INCREMENT BY 1
START WITH 11114992
CACHE 20
NOORDER
NOCYCLE
NOPARTITION;

Truncate data and if insert new row column increment from 1

I have table with two rows which one ID with auto increment and there are much row last number ID is 89. And then I truncate data/row in the table. And then I insert row again.
But number ID from 90 not from 1 (one). If in mysql if I truncate data in table auto increment start from 1 (one) again. So how in oracle I want to ID autoincrement from one again. Thanx.
Below step when I create table:
// create table;
CREATE TABLE tes (
id NUMBER NULL,
ip_address varchar2(25) NOT NULL
PRIMARY KEY (id)
);
// and create increment;
CREATE SEQUENCE tes_sequence START WITH 1 INCREMENT BY 1;
// and create trigger;
CREATE OR REPLACE TRIGGER tes_trigger
BEFORE INSERT
ON tes
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT tes_sequence.nextval INTO :NEW.ID FROM dual;
END;
Oracle sequence is a separate object and is not connected with table. If you need to start sequence after truncating a table you need to alter the sequence. Have a look here: How do I reset a sequence in Oracle?

locking on delete from parent table instead of error

I have Oracle Database 11g Enterprise Edition Release 11.2.0.1.0.
I have parent table t1 and t2 with foreign key which references t1(col1).
What I'm wondering is why locking is there?
Please check what I've done...
session 1
SQL> create table t1(col1 char(1), primary key(col1));
Table created.
SQL> insert into t1 values('1');
1 row created.
SQL> insert into t1 values('2');
1 row created.
SQL> insert into t1 values('3');
1 row created.
SQL> insert into t1 values('4');
1 row created.
SQL> insert into t1 values('5');
1 row created.
SQL> commit;
Commit complete.
SQL> create table t2(col1 char(1), col2 char(2), foreign key(col1) references t1(col1));
Table created.
SQL> insert into t2 values('1','0');
1 row created.
SQL> commit;
Commit complete.
SQL> update t2 set col2='9'; --not committed yet!
1 row updated.
session 2
SQL> delete from t1; -- Lock happens here!!!
session 1
SQL> commit;
Commit complete.
session 2
delete from t1 -- The error occurs after I commit updating query in session 1.
*
ERROR at line 1:
ORA-02292: integrity constraint (KMS_USER.SYS_C0013643) violated - child record found
Could anyone explain me why this happens?
delete from t1; tries to lock the child table, T2. If the session is waiting on the entire table lock it can't even try to delete anything yet.
This unusual locking behavior occurs because you have an unindexed foreign key.
If you create an index, create index t2_idx on t2(col1);, you will get the ORA-02292 error instead of the lock.
The lock is coming from your line: insert into t2 values('1','0'); The lock does not happen when you delete from t1 in session 2.
Think about it. Once you insert this row in session 1, there is a reference from t2.col1 back to t1.col1. The foreign key has been validated at that point and Oracle knows that the '1' exists in t1. If session 2 could delete that row from t1, then session 2 would have an uncommitted row in t2 that had an invalid reference to t1, which doesn't make any sense.

Resources