How to get previous data before commit? - sql

Is there a way to get the previous values ​​that were in a specific table before the current changes and before we commit changes, and without store data in backup table.
Maybe that way it will be more clear:
stage 1: change data in table X without commit
stage 2: get data before stage 1 without store it in backup folder
stage 3: commit stage 1 changes.

Depending on how your transaction is designed you could place logic in a trigger. Triggers have access to old and new values. See Oracle Documentation for more information (:old and :new).
CREATE OR REPLACE TRIGGER test_trigger
BEFORE DELETE OR UPDATE ON test_table
FOR EACH ROW
BEGIN
INSERT INTO backup_table (col1, col2) VALUES (:old.col1, :old.col2);
END;

The RETURNING clause on an UPDATE should be able to do this for you. Unfortunately, I don't know the Oracle syntax for referring to the previous value (in PostgreSQL this would be OLD.column_name) but the docs make it sound as if your needs are supported.

Related

Why would sql plus not save a new row even after using commit?

I'm learning DML for my class and trying to insert new rows and using oracle 18c. Even after I use the commit command the row isn't saved. After I close the sql plus window and open it again and type
SELECT*FROM acctmanager;
it still says No rows selected. What could be the problem?
[enter image description here]
My bet is ACCTMANAGER is a global temporary table. These are permanent data structures, but any data persists only for the duration of a transaction or a session, depending on how the table was created. Find out more.
I am able to access the row after commit and before closing.
This strong suggests the table was created with ON COMMIT PRESERVE ROWS clause.
Easy enough to check:
select table_name, temporary
from user_tables
where table_name = 'ACCTMANAGER'

Keep a shadow copy of a table while retaining records removed from the original

This is probably laughably easy for an SQL expert, but SQL (although I can use it) is not really my thing.
I've got a table in a DB. (Let's call it COMPUTERS)
About 10.000 rows. 25 columns. 1 unique key: Column ASSETS.
Occasionally an external program will delete 1 or more of the rows, but isn't supposed to do that, because we still need to know some info from those rows before we can really delete the items.
We can't control the behavior of the external application so we came up with a different idea:
We want to create a second identical table (COMPUTERS_BACKUP) and initially fill this with a one-on-one copy of COMPUTERS.
After that, once a day copy new records from COMPUTERS to COMPUTERS_BACKUP and update those records in COMPUTERS_BACKUP where the original in COMPUTERS has changed (ASSETS column will never change).
That way we keep the last state of a record deleted from COMPUTERS.
Can someone supply the code for a stored procedure that can be scheduled to run once a day? I can probably figure this out myself, but it would take me several hours or so and I'm very pressed for time.
just create a trigger for insert computers table
CREATE TRIGGER newComputer
ON [Computers]
AFTER INSERT
Begin
INSERT INTO COMPUTERS_BACKUP
SELECT * FROM Inserted
End
It'll work when you insert new computer to computers table and it'll also insert the record to bakcup table
When you update computers you could change computers backup too with update trigger
CREATE TRIGGER newComputer
ON [Computers]
AFTER UPDATE
Begin
//can access before updating the record through SELECT * FROM Deleted
//can access after updating the record through SELECT * FROM Inserted
UPDATE Computers_BACKUP SET
(attributes) = inserted.(attribute)
WHERE id = inserted.id
End
At the end I guess you don't want to delete the backup when original record is deleted from computers table. You can chech more examples from msdn using triggers.
When a record removed from computers table
CREATE TRIGGER computerDeleted ON [Computers] AFTER DELETE
Begin
INSERT INTO Computers_BACKUP
SELECT * FROM Deleted
End
Besides creating triggers, you may look into enabling Change Data Capture, which is available in SQL Server Enterprise Edition. It may be an overshot, but it should be mentioned and you may find it useful for other tables and objects.
IMHO a possible solution, if you never delete records (only update) from that table in your application, can be to introduce an INSTEAD OF DELETE trigger
CREATE TRIGGER tg_computers_delete ON computers
INSTEAD OF DELETE AS
DELETE computers WHERE 1=2;
It will prevent the deletion of the records.
Here is SQLFiddle demo.
A trigger for Before Delete event can help you to guard this table:
CREATE TRIGGER backup_row_before_delete ON COMPUTERS_Table FOR Delete
as
INSERT INTO Computers_Backup
SELECT deleted.* from deleted
You can change deleted.* for deleted.col1, deleted.col2 if you want to keep certain columns only.
will delete 1 or more of the rows, but isn't supposed to do that
Then you have permission and integrity issues.
You can most certainly use a trigger to record deletions (and updates of course) but I would not recommend you use it purely to keep a copy of stuff you didn't want deleted in the first place!
Remove delete permissions if you have to or beef up your data integrity if you can. Without your schema it's hard to tell exactly how though.
Finally, use your (INSTEAD OF) trigger to check whatever conditions you need to prevent the delete when appropriate.

Oracle Trigger to write all Insert/Updates to File

I want to place a trigger on a table which writes all inserted / updated Data to an additional log file for processing with an external tool.
Is the a way to accomplish that?
You need to create triggers that execute after the table row has been altered and write to a log file using the UTL_FILE package.
The UTL_FILE package info can be found here:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_file.htm
And Trigger documentation can be found here:
http://docs.oracle.com/cd/B10501_01/appdev.920/a96590/adg13trg.htm
There is a similar answer to what you are looking for here:
http://bytes.com/topic/oracle/answers/762007-trigger-output-text-file
More info on writing to a file using PL/SQL here:
http://www.devshed.com/c/a/Oracle/Writing-to-Text-Files-in-Oracle-PLSQL/
Hope it helps...
I would avoid writing out to the file system at DML time, but would pull out the data in a batch process each night (or whatever frequency).
From your OP, its not clear if you need the "new" data after the update, or the "old" data before the update. If you just want the latest data, why not just add a modified_date field (date or timestamp type) and update that via a trigger.
create table test
(
id number,
val varchar2(100),
modified_date date default sysdate not null
)
;
CREATE OR REPLACE TRIGGER TR_TEST_BU
BEFORE UPDATE
ON TEST REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
begin
:new.modified_date := sysdate;
end;
insert into test(id, val) values (1, 'Insert 1');
commit;
select * from test;
exec dbms_lock.sleep(5);
update test set val = 'Update 1' where id = 1;
commit;
select * from test;
If you need the old data before the update (or you care about deleted data), then you'll modify the trigger to write old or deleted values to a history table, then extract the data from there.
Also note that adding a trigger to a table will slow down associated DML activity. Some shops wish to avoid this by replacing the triggers with business logic rules ("all apps must update modifed_date" edict), which usually leads to inconsistent data (or worse) from what I've seen.
Yes, here you have an example of the update part.
You simply need to do a similar one to the insert part.

Need a sql statement to do upate and insert at the same time

I need a sql statement, to insert a new row in one database table and update an existing row in another database table based on some conditions.
Is there a way to do this? To insert a row in one table and update a row in another database table in one sql statement?
Thanks in advance!
Yes, they are called Transactions, and are implemented with START TRANSACTION and COMMIT/ROLLBACK with something like:
START TRANSACTION;
INSERT INTO ...
UPDATE table2 SET name='TOTO' WHERE type=1;
COMMIT;
EDIT
This is not in fact one SQL query, but the operation is done atomically - and I think that is what you need.
A single SQL statement allows you to update one table, not several; if that statement is a MERGE then you can specify insert/update/delete actions but still targeting just the same one target table.
If you just want consistency, use transactions; until a transaction is committed, changes within it are not visible to the outside world.
If you want that a single update (which you cannot control) resulted in a coordinated insert, use an on update trigger in the table being updated. The trigger would insert appropriate row(s) into other table(s).
You can use Trigger to update second table on insert of first table
Yes, it's possible with stored procedures.
Watch this: Stored procedures

SQL Server 2005 Insert Trigger with Update Statement

I am currently not in a location to test any of this out but would like to know if this is an option so I can start designing the solution in my head.
I would like to create an insert trigger on a table. In this insert trigger, I would like to get values from the inserted virtual table and use them to UPDATE the same table. Would this work or would we enter some kind of infinite loop (even though the trigger is not for update commands).
As an example if a row was inserted (which represents a new rate/cost for a vendor) I would like to update the same table to expire the old rate/cost for that vendor. The expiration is necessary vs updating the record that already exists so a history of rates/costs can be kept for reporting purposes (not to mention that the current reporting infrastructure expects this type of thing to happen and we are migrating current reports/data to SQL Server).
Thanks!
If you have only an INSERT trigger and no UPDATE trigger then there isn't any problem, but I assume you want to catch also UPDATEs and perhaps even DELETEs.
The INSTEAD OF triggers are guaranteed not to behave recursively:
If an INSTEAD OF trigger defined on a
table executes a statement against the
table that would ordinarily fire the
INSTEAD OF trigger again, the trigger
is not called recursively
With and INSTEAD OF trigger you must do both the original INSERT and the UPDATE you desire.
This doesn't sound like it would cause any problems to me, providing you're not doing an INSERT in another UPDATE trigger.