Stored Procedure: only commit when successful - sql

I would like to build a stored procedure that:
1. truncates Table A
2. truncates Table B
3. inserts (lots) of rows in table A
4. inserts (lots) of rows in table B
The stored procedure should only commit the statements after step 4 so that the tables are not locked and experience no down time.
If an error occurs (for instance in step 4) all changes must be rolled back. I tried writing it myself but it committed after each statement.
create or replace PROCEDURE upall as
BEGIN
execute immediate 'truncate table MAIN.SET';
insert into MAIN.SET select * from MAIN.SET_STAG;
execute immediate 'truncate table MAIN.TYPE';
insert into MAIN.TYPE select * from MAIN.TYPE_STAG;
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;

In Oracle, TRUNCATE TABLE is a DDL statement that cannot be used in a transaction (or, more accurately, cannot be rolled back).If there is a transaction in progress when the statement is executed, the transaction is committed and then the TRUNCATE is executed and cannot be undone.
Try DELETE FROM YourTable and finally update stats of your table (since DELETE will outdate it)
It will looks like :
CREATE or REPLACE PROCEDURE upall as
BEGIN
delete from MAIN.SET;
insert into MAIN.SET select * from MAIN.SET_STAG;
delete from MAIN.TYPE;
insert into MAIN.TYPE select * from MAIN.TYPE_STAG;
COMMIT;
EXEC DBMS_STATS.GATHER_TABLE_STATS ('MAIN', 'SET');
EXEC DBMS_STATS.GATHER_TABLE_STATS ('MAIN', 'TYPE');
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;

Related

Multiple batches in a SQL transaction

I want to create several stored procedures (or user-defined functions) within a transaction. CREATE PROCEDURE statement must be the only statement in the batch, so I have to use following template:
BEGIN TRANSACTION MyTransaction
USE MyDatabase
GO
CREATE PROCEDURE A
AS
-- The body of procedure A
GO
CREATE PROCEDURE B
AS
-- The body of procedure B
GO
CREATE PROCEDURE C
AS
-- The body of procedure C
GO
COMMIT TRANSACTION MyTransaction
The problem appears if an error occurs within one of the batches. For example, if error occurs while procedure B is creating, MyTransaction with procedures A and B will be rolled back. But the script will continue to run. So, the procedure C will be created.
Also it's not possible to skip the statement using GOTO and ##error counter checking, because it cannot go to a label outside the batch in which GOTO is placed.
How to prevent any database changes if an error occurs?
You can try to do something like this with SET XACT_ABORT ON.
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
GO
PRINT N'Creating [Proc A]'
GO
-------------------------------------------------------------------------
CREATE PROCEDURE A
AS
-- The body of procedure A
GO
-------------------------------------------------------------------------
IF ##ERROR<>0 AND ##TRANCOUNT>0 ROLLBACK TRANSACTION
GO
IF ##TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO
PRINT N'Creating [Proc B]'
GO
-------------------------------------------------------------------------
CREATE PROCEDURE B
AS
-- The body of procedure B
GO
-------------------------------------------------------------------------
IF ##ERROR<>0 AND ##TRANCOUNT>0 ROLLBACK TRANSACTION
GO
IF ##TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO
IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
GO
IF ##TRANCOUNT>0 BEGIN
PRINT 'The database update succeeded'
COMMIT TRANSACTION
END
ELSE PRINT 'The database update failed'
GO
DROP TABLE #tmpErrors
GO

Rollback in procedure called over dblink

I have strange situation
It's bit hard to explain but I'll do my best
There are 3 different database included
From DB1 I call function on DB2 (over dblink)
That procedure calls another procedure that inserts data into table on DB3
Function on DB2 has EXCEPTION handle that should rollback everything that it did in case of exception
I did example run, and everything went well (there was no error) but insert from procedure 3 was not rollbacked and I have to rollback from DB1 to truly rollback
If i commit from db1, row is inserted
Am I doing something wrong and is there a way to rollback directly from function on db2
Here is some example code:
--DB1
PROCEDURE 1
BEGIN
x := function2#dblink_to_db2();
END;
--DB2
FUNCTION 2
BEGIN
procedure3();
RAISE SOME EXCEPTION;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
do_something_else();
RETURN 0;
END;
PROCEDURE 3
BEGIN
INSERT INTO tableA#dblink_to_db3 VALUES ... ;
END;
So no error is raised but insert into table on db3 is not rollbacked
You must be having a commit somewhere in your code either before the raise exception or in Procedure3. I just tested the below code and it rolledback everything before the exception. Please ignore the naming conventions, had to go due to time constraint.
Database 3
CREATE TABLE temp
(col1 NUMBER);
create or replace procedure testp(i number)
as
BEGIN
INSERT INTO temp VALUES (i);
END;
/
Database2
CREATE OR REPLACE FUNCTION DLR_TRANS.testf(i number)
return number
as
e exception;
BEGIN
testp#TO_DB3(i);
RAISE e;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RETURN 0;
END;
/
database1
declare
x number;
BEGIN
x := testf#TO_DB2(15);
DBMS_OUTPUT.PUT_LINE ( 'x = ' || x );
commit;
END;
x returns 0 due to exception in DB2's function.
And below is the data when I ran the below statement on DB3
select * from temp;
Hope this helps
The problem is that you have "handled" the exception in [function 2]. You should not put the exception block in [function 2] at all. And let the exception propagate up to [procedure 1]. Here you will implicitly or explicitly rollback.
If you must have an exception block in [function 2] then you should have a [raise] at the end. Handling the exception like this is saying [I have handled it and for all practical purposes the caller of this should not think anythibg bad has happened]

How to save table progress in postgresql between transaction?

Tables are updating in loop, but if error come in one of table than transaction failed and all the tables data updated is gone so provide me the solution in which each time any table is update that its progress can save.
d0
$$
declare g record;
declare tablename varchar(50);
BEGIN
--fetching tablename from catalog.table
for g in execute formate ('select table_name from catalog.table');
loop
tablename= lower(g.tablename);
--passing tablename to function for some execution
execute'select function('''||tablename||''')';
end loop;
end;
$$
The transaction won't fail if you trap the error.
BEGIN
execute your query
EXCEPTION WHEN unique_violation OR foreign_key_violation OR ... THEN
END;
When a function or codeblock is executed there is always already a transaction either created explicitly with a BEGIN or automatically. The BEGIN of the exception block acts as a SAVEPOINT in the transaction. When the error is trapped by the EXCEPTION part only the work after the BEGIN is lost because it rollsback to the savepoint.
When you let an error escape from the function a rollback of the whole transaction is done.
For details see the manual.
BTW. postgresql 9.1 is not being maintained you should consider upgrading.

Procedure for truncate and insert in sql

I have a table- Event_name.
select * from Event_name
I have to truncate the data and insert fresh data daily.
Can someone tell me how to write a stored procedure for truncating and inserting data into table-Event_name?
The generic approach is
Create procedure proc_name
as
Begin
Truncate table Event_name;
insert into Event_name(col_list)
select col_list from source_table;
End;
Try this:
create or replace procedure myProcedure
AS
BEGIN
execute immediate 'truncate table Event_name';
insert into Event_name select * from Event_name;
END;

PLSQL Call Procedure If Exists Clause

I'm using Oracle 9i.
Cue pseudo-code!
I have Stored Procedure A:
PROCEDURE stored_proc_a
IS
BEGIN
insert into...
END;
Then, I have Stored Procedure B:
PROCEDURE stored_proc_b
IS
BEGIN
stored_proc_a
WHERE NOT EXISTS (SELECT * FROM...);
END;
As you can see from my pseudo-code, I would like to call procedure A from procedure B, if a given row does not exist in a table.
I can't find any documentation that would suggest that the WHERE EXISTS clause can be used with a procedure call (the examples show its use with INSERT, UPDATE and DELETE).
Can I use WHERE EXISTS with a procedure call, and if not, what would be the correct code to do a procedure call based on the absence of a particular record in a table?
The correct way of doing this is the following:
PROCEDURE stored_proc_b
IS
num_rows number;
BEGIN
select COUNT(*) into num_rows
FROM my_table
WHERE my_table.xyz = 123; -- (whatever is appropriate)
if num_rows < 1
then
stored_proc_a;
end if;
END;
Figured this out thanks to Nicholas Krasnov and WBAR for their info on other posts.
Another way of achieving the same, in case you you want to call it for multiple rows and want to use data from Table in procedure B-
PROCEDURE stored_proc_b
IS
BEGIN
FOR rec IN (SELECT COL1 FROM <<TABLE1>> T1 WHERE NOT EXISTS (SELECT * FROM <<TABLE2>> T2...WHERE T1.JOIN_COL = T2.JOIN_COL))
LOOP
stored_proc_a;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
rollback;
END;