Do I need to COMMIT when interleaving DDLs and DMLs? - oracle

In Oracle, I am invoking the following querys (not from sqlplus) as part of an installation script:
ALTER TABLE MYTABLE DISABLE CONSTRAINT PFTATTRS_ATTR_FK;
INSERT INTO MYTABLE (PTF_ID, ATTR_ID) VALUES (1, 5);
ALTER TABLE MYTABLE ENABLE CONSTRAINT PFTATTRS_ATTR_FK;
As you see I'm interleaving DMLs (that require COMMIT) with DDLs (that are auto-committed).
My doubt is: do I need to commit the DMLs before every DDL, or is it safe to do one big commit at the end of my script?

See here.
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:7072180788422
https://community.oracle.com/message/10310617
Each DDL statement will commit all previous uncommitted DML statements.

Related

If a trigger on an Oracle DB table fails to execute successfully or does not compile, can it impact the base table?

let's say there are tables a and b.
There is an AFTER INSERT trigger on a, that copies the row data to b.
If, during the execution of the trigger, there is an error does it impact table a in any way?
If the trigger does not compile, because it's ill-defined, does it impact table a?
I want to add a trigger to a table that is 'not mine'. I want to evaluate the risk that this can potentially pose.
Cheers
============== edit ================
I verified that - by handling the error (as suggested in the reply) - it now does not impact the base table.
create table tableA (column1 number);
create table tableB (column1 number, CONSTRAINT constraintName PRIMARY KEY (column1));
create or replace TRIGGER tableA_trigger
AFTER INSERT ON tableA
FOR EACH ROW
DECLARE
--
BEGIN
insert into tableB values (1);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error occured but ignored.');
END;
insert into tableA values (1);
insert into tableA values (1);
After that, tableA had two records, tableB only 1.
Without the exception handling, both tables would only have one record each, and after the first insert it would have shown an exception in the SQL Developer window.
Yes, it will impact the base table.
If there is error in insert trigger it will not allow to insert any record in base table. Same applies to all type of triggers.
Also, Adding DML triggers to tables affects the performance of DML statements on those tables.
According to oracle documentation:
If a predefined or user-defined error condition or exception is raised
during the execution of a trigger body, then all effects of the
trigger body, as well as the triggering statement, are rolled back
(unless the error is trapped by an exception handler). Therefore, a
trigger body can prevent the execution of the triggering statement by
raising an exception. User-defined exceptions are commonly used in
triggers that enforce complex security authorizations or integrity
constraints.

Savepoints in Oracle global temporary tables

I read that savepoints in Oracle global temporary tables delete all the data, but when I tested on Oracle 11g they worked like heap tables. Can anybody explain?
insert into table_1 values('one');
insert into table_1 values('two');
savepoint f1;
insert into table_1 values('three');
insert into table_1 values('four');
rollback to f1;
-- the records in table are 2 records just like heap tables, but I read that
-- savepoints in GTT truncates all the data
Where did you read this? I suspect not in the Oracle SQL Reference. So the explanation is simple: the author of that assertion hadn't tested the behaviour of global temporary tables. Either that or you were reading a description of some other SQL implementation, such as DerbyDB.
For the sake of completeness, let's rule out the role of transaction or session scope. Here are two global temporary tables:
create global temporary table gtt1
( col1 varchar2(30) )
ON COMMIT PRESERVE ROWS
/
create global temporary table gtt2
( col1 varchar2(30) )
ON COMMIT DELETE ROWS
/
Let's run your experiment for the one with session scope:
SQL> insert into gtt1 values('one');
1 row created.
SQL> insert into gtt1 values('two');
1 row created.
SQL> savepoint f1;
Savepoint created.
SQL> insert into gtt1 values('three');
1 row created.
SQL> insert into gtt1 values('four');
1 row created.
SQL> rollback to f1;
Rollback complete.
SQL> select * from gtt1;
COL1
------------------------------
one
two
SQL>
Same result for the table with transaction scope:
SQL> insert into gtt2 values('five');
1 row created.
SQL> insert into gtt2 values('six');
1 row created.
SQL> savepoint f2;
Savepoint created.
SQL> insert into gtt2 values('seven');
1 row created.
SQL> insert into gtt2 values('eight');
1 row created.
SQL> rollback to f2;
Rollback complete.
SQL> select * from gtt2;
COL1
------------------------------
five
six
SQL>
Actually this is not surprising. The official Oracle documentation states:
"The temporary table definition persists in the same way as the definitions of regular tables"
Basically they are heap tables. The differences are:
the scope (visibility) of the data
the tablespace used to persist data (global temporary tables write to a temporary tablespace).
I think you misunderstand - if you rollback to a savepoint then Oracle should undo all the work done after the savepoint (while still keeping any uncommitted work that was done prior to the savepoint).
For a temporary table, Oracle lazily allocates storage (a temporary segment for your session) when you put stuff in, and that when the data is done with (either at the end of the session, or at the end of the transaction, depending on the type) it can just deallocate the storage rather than individually deleting the rows, rather like what happens when you TRUNCATE a normal table.
I was interested to find out what happened if you had a savepoint before any data was put in, and rolled back to that savepoint - would Oracle deallocate the storage or would it keep the storage and delete the rows from within it?
It turns out the former - it behaves like a truncate.
SAVEPOINT f0;
SELECT * FROM v$tempseg_usage; -- Should show nothing for your session
insert into table_1 values('one');
insert into table_1 values('two');
SELECT * FROM v$tempseg_usage; -- Should show a DATA row for your session
savepoint f1;
insert into table_1 values('three');
insert into table_1 values('four');
rollback to f1; -- Undo three and four but preserve one and two
SELECT * FROM v$tempseg_usage; -- Still shows a DATA row for your session
rollback to f0; -- Undo all the inserts
SELECT * FROM v$tempseg_usage; -- row for your session has gone
The reason this matters is that when you do a normal delete - rather than a truncate - then any full scan of the table will still have to sift through all the data blocks to see if they have any data in. DML against an empty table can potentially incur a lot of I/O if the table had a lot of data in it at some time before!
I am trying to speed up some code that is doing exactly that - it isshoving some stuff into a temporary table as a scratchpad, partly so it canjoin to a permanent table, and returning a result to its caller. The temporary table is only for the benefit of this routine, so it's safe to clear it down at the end of the routine, but it might be called many times within a parent transaction, so I can't truncate (TRUNCATE is DDL and so commits the transaction) but I can't not clear it down either, or invocations within the same transaction will pick up one anothers' rows. Clearing down by a DELETE is causing quite a bit of overhead, especially as there is no index on the table and so selects against it will always full scan.
The option I am exploring is to have a SAVEPOINT at the start of the routine, do my temporary work, and then roll back to the savepoint just before it returns the result. Another option might be to put the routine inside an autonomous transaction, but it would mean porting C code to a PL/SQL stored procedure, and wouldn't work anyway if the temporary table needs to be joined to uncommitted data inserted by the caller.
Note that I did my research in 12c - there have been some improvements to temporary tables in this release (see https://oracle-base.com/articles/misc/temporary-tables) but I don't think that affects the behaviour wrt savepoints.

Rollback command not working - Oracle

I dropped a table and tried to rollback, but to no use. Will it ever work like this or am I playing wrong here?
As from most of the comments I am clear that DDL statements cannot be undone by rollback but only by FLASHBACK.
I tried undoing
DELETE FROM STUDENT;
It still it can't be undone:
My order of execution was
INSERT,
DELETE FROM ,
ROLLBACK.
I don't believe rollback will undo schema changes.
ROLLBACK without a savepoint qualifier will roll back the entire current transaction.
For DDL statements, there is no current transaction to rollback. The DDL statement implicitly generates a COMMIT before the statement starts and after it completes. So if you issue a ROLLBACK following a DROP, no work has been done in the current transaction so there is nothing to roll back.
For DML statements, you'll roll back the entire current transaction. If you do
INSERT
DELETE
ROLLBACK
your transaction begins when you execute the INSERT operation. So when you issue the ROLLBACK, you are rolling back both the INSERT and the DELETE so you're back to having no data in the table (assuming you started with no data). If you COMMIT after the INSERT then the next transaction would begin with the DELETE and your ROLLBACK will only roll back the DELETE operation. Alternately, you can declare a savepoint after the INSERT and roll back to the savepoint
SQL> create table foo( col1 number );
Table created.
SQL> insert into foo values( 1 );
1 row created.
SQL> savepoint after_insert;
Savepoint created.
SQL> delete from foo;
1 row deleted.
SQL> rollback to savepoint after_insert;
Rollback complete.
SQL> select * from foo;
COL1
----------
1
Rollback does not undo schema changes, but to undo drop table operations you can check:
http://docs.oracle.com/cd/B19306_01/backup.102/b14192/flashptr004.htm
From the documentation:
Oracle Database implicitly commits the current transaction before and after every DDL statement.
This means that you cannot ROLLBACK a DDL statement (that is, a schema change).
Rollback will never undo Data Definition commands such as drop table alter table etc.
Dropping a table changes the structure of the database (using DDL statements like CREATE, DROP, ...).
COMMIT and ROLLBACK only work on the data which is exchanged with the database using DML statements (like INSERT, UPDATE, ...).
So, no it will never work like this.
To rollback ddl changes you need to use Flashback.
Rollback:
Discard all pending changes by using the ROLLBACK statement. Following a ROLLBACK statement:
Data changes are undone.
The previous state of the data is restored.
The locks on the affected rows are released.
Example
While attempting to remove a record from the TEST table, you can accidentally empty the table. You can correct the mistake, reissue the proper statement, and make the data change permanent.
DELETE FROM test;
25,000 rows deleted.
ROLLBACK;
Rollback complete.
DELETE FROM test
WHERE id = 100;
1 row deleted.
SELECT *
FROM test
WHERE id = 100;
No rows selected.
COMMIT;
Commit complete
After giving commit we can't rollback.

A simple scenario in which the effect of the ROLLBACK command can not be observed in Oracle 9i

Suppose, on the SQL prompt in Oracle 9i, I'm creating a simple table say orderStatus with only two columns with the following CREATE SQL command.
CREATE TABLE orderStatus(statusID varchar2(6) primary key, status varchar2(15));
just then I'm entering four rows with the following INSERT INTO commands.
INSERT INTO orderStatus VALUES("S0001", "Fulfilled");
INSERT INTO orderStatus VALUES("S0002", "Back order");
INSERT INTO orderStatus VALUES("S0003", "In process");
INSERT INTO orderStatus VALUES("S0004", "Cancelled");
On successful execution of the above commands, I'm issuing a COMMIT command to complete the current transaction.
COMMIT;
just then I need to fire the following DELETE command.
DELETE FROM orderStatus WHERE statusID="S0004";
which deletes one row with the statusID S0004. The table now contains only 3 rows.
Suppose, I need to add the NOT NULL constraint to the status column which requires the following ALTER TABLE command.
ALTER TABLE orderStatus MODIFY(status varchar(15) NOT NULL);
The table orderStatus will be altered to add a NOT NULL constraint to the status column.
Now, suppose I execute a ROLLBACK command on the SQL prompt which causes the current transaction to finish with all the data which are previously affected in this table to be undone. With this, the previously deleted row with the DELETE command above should be undone and the table should now contain the original four rows but the effect of ROLLBACK can not be observed and the table would have only three rows. Why does this happen?
DDL (CREATE TABLE and ALTER TABLE, for example) issues an implicit commit before the statement is executed and after the statement completes. You cannot roll back DDL and you cannot roll back DML changes (like your DELETE) after the implicit commits your DDL causes.
This is why you do not generally want to mix DDL and DML.

Oracle DDL in autonomous transaction

I need to execute a bunch of (up to ~1000000) sql statements on an Oracle database. These statements should result in a referentially consistent state at the end, and all the statements should be rolled back if an error occurs. These statements do not come in a referential order. So if foreign key constraints are enabled, one of the statements may cause a foreign key violation even though, this violation would be fixed with a statement that would be executed later on.
I tried disabling foreign keys first and enabling them after all statements were executed. I thought I would be able to roll back when there was an actual foreign key violation. I was wrong though, I found out that every DDL statement in Oracle started with a commit, so there was no way to rollback the statements this way. Here is my script for disabling foreign keys:
begin
for i in (select constraint_name, table_name from user_constraints
where constraint_type ='R' and status = 'ENABLED')
LOOP execute immediate 'alter table '||i.table_name||' disable constraint
'||i.constraint_name||'';
end loop;
end;
After some research, I found out that it was recommended to execute DDL statements, like in this case, in an autonomous transaction. So I tried to run DDL statements in an autonomous transaction. This resulted in the following error:
ORA-00054: resource busy and acquire with NOWAIT specified
I am guessing this is because the main transaction still has DDL lock on the tables.
Am I doing something wrong here, or is there any other way to make this scenario work?
There's several potential approaches.
The first thing to consider is that whatever you do at the table level will apply to all sessions using that table. If you haven't got exclusive access to that table, you probably don't want to drop/recreate constraints, or disable/enable them.
The second thing to consider is that you probably don't want to be in a position of rolling back a million inserts/updates. Rolling back can be SLOW.
Generally I would load into a temporary table. Then do a single INSERT from the temporary table into the destination table. As a single statement, Oracle will apply all the check constraints at the end.
If you can't go through a temporary table (eg updates to existing data), before starting make the constraints deferrable initially immediate. Then, within your session,
SET CONSTRAINTS emp_job_nn, emp_salary_min DEFERRED;
You can then apply the changes and, when you commit, the constraints will be validated.
You should aquaint yourself with DML error logging as it can help identify any rows causing violations.

Resources