Can this update cause a deadlock in oracle 10g - oracle

I came across this update statement and was wondering how the internal working is. It updates a column which also is used in the where clause of the update.
Should this be ideally done in two steps, or does oracle takes care of it automatically?
UPDATE TBL1 SET DATE1=DATE2 WHERE DATE2> DATE1

Oracle takes care of it automatically. Effectively when it runs the update, Oracle performs the following steps:
Queries the table - i.e. evaluate the WHERE clause predicate for each row in the table
For each row that is returned by step 1, update it as per the SET clause. The values of each column are those that were fetched.
For this reason, it is perfectly possible to run an update like this which swaps the values of columns:
UPDATE TBL1 SET DATE1=DATE2, DATE2=DATE1 WHERE DATE2 > DATE1;
The update might be blocked if another session tries to update or delete one of the same rows. Deadlocks are possible but Oracle automatically resolves these by rolling back one of the sessions and raising an exception.

Related

Statement-level trigger in Oracle

I can't fully understand how a statement-level trigger works. It executes once for each transaction right? If I have this AFTER INSERT trigger and what it does inside is that it updates one specific column if it meets the condition (ex for column status, UPDATE table_name SET STATUS = "Single" WHERE COLUMN is null).
Are the newly inserted data only the ones get to be affected? Or every data in the table that has this null value in column status. I'll be glad hearing your knowledge about this.
A statement level trigger will fire once after the triggering statement has run, unlike a row level trigger which fires for each affected row.
After statement triggers are generally used to do processing of the set of data - e.g. logging into a table, or running some post-statement processing (usually a procedure).
If you're wanting to update a value in every affected row, then I would advise using a before row level trigger. The update statement in your question would affect all rows where the COLUMN column is null.
Whether a trigger is actually the right thing to use is debatable. However, I would recommend you look at the documentation and also this Oracle-base article to gain a better understanding of how triggers work and when you might use them.

Transactional level for subqueries

I want to remove duplicate rows from the table and found this solution at SO (Removing duplicate rows from table in Oracle)
DELETE FROM your_table -- step 2
WHERE rowid not in
(SELECT MIN(rowid) -- step 1
FROM your_table
GROUP BY column1, column2, column3);
What will happen if there are rows inserted after Step 1 but before Step 2, will they be deleted as well? If yes, what transactional level should I use to avoid it?
From the concepts guide:
In the read committed isolation level, which is the default, every
query executed by a transaction sees only data committed before the
query—not the transaction—began.
...
A query in a read committed transaction avoids reading data that
commits while the query is in progress.
...
A consistent result set is provided for every query, guaranteeing data
consistency, with no action by the user. An implicit query, such as a
query implied by a WHERE clause in an UPDATE statement, is
guaranteed a consistent set of results. However, each statement in an
implicit query does not see the changes made by the DML statement
itself, but sees the data as it existed before changes were made.
The last paragraph applies to your case as well, just for DELETE rather than UPDATE. Your statement - the delete itself and the subquery are a single statement - is isolated so it won't be affected by any changes made in any other sessions or transactions. You won't 'see' any rows inserted elsewhere while your statement is executed, whether they are commited or not.
So you don't need to change from the default isolation level.

Does Oracle update columns when the data values are the same?

If I have a column called NAME and it has a value of "CLARK" and I run an update statement
update table1 set name = 'CLARK';
Does Oracle actually update the column or does it ignore the update command since the values are the same?
I found this question (Oracle, how update statement works) and the first answer implies that an update occurs even if the values are equal. I also tried it in SQL Developer and it ran but I don't know if an update truly occurred.
Thanks in advance.
Yes, Oracle does update the column even if it the same.
In a really simple example, this makes no difference. But consider the following:-
When a record is updated, a lock is obtained on that record for the updating session,
When a record is updated, triggers on the table would fire
This aspects of the update show that the column is actually updated.
Of course, perhaps there are some optimisations when the value is the same, but these are not visible to you as a user of Oracle.
Yes, all row are updated and all triggers fired, even if the actual values doesn't change.

Oracle, 2 procedures avoid deadlock

I have two procedures that I want to run on the same table, one uses the birth date and the other updates the name and last name taken from a third table.
The one that uses the birthday to update the age field runs all over the table, and the one that updates the names and last name only updates the rows that appear on the third table based on a key.
So I launched both and got deadlocked! Is there a way to prioritize any of them? I read about the nowait and skip locked for the update but then, how would I return to the ones skipped?
Hope you can help me on this!!
One possibility is to lock all rows you will update at once. Doing all updates in a single update statment will accomplish this. Or
select whatever from T
where ...
for update;
Another solution is to create what I call a "Gatekeeper" table. Both procedures need to lock the Gatekeeper table in exclusive mode before updating the table in question. The second procedure will block until the first commits but won't deadlock. In 11g you can create a table with no space allocated.
A variation is to insert a row in the Gatekeeper. Then lock only that row with select for update. Then you can use the Gatekeeper in other situations.
I would guess that you got locked because the update for all the rows and the update for a small set of rows accessed rows in different orders.
The former used a full scan and reached Raw A first, then went on to other rows, eventually trying to lock Row B. However, the other query was driven from an index or a join and already had Row B locked, and was off to lock Row A when it found it was already locked.
So, the fix: firstly, having an age column that needs to be constantly modified is a really bad idea. Perhaps it was done to allow indexing of age, but with a correctly written query an index on date of birth will let you find the same records just as quickly. You've broken normalisation rules and ended up coding yourself a deadlocking application. Hopefully you are only updating the rows that need to be updated, not all of them regardless -- I mean, that would just be insane.
The best solution is to get rid of that design flaw.
The not so good solution is to deconflict your queries by running them at different times or by using DBMS_Lock so that only one of them can run at any time.

oracle and creating history

I am working on a system to track a project's history. There are 3 main tables: projects, tasks, and clients then 3 history tables for each. I have the following trigger on projects table.
CREATE OR REPLACE TRIGGER mySchema.trg_projectHistory
BEFORE UPDATE OR DELETE
ON mySchema.projects REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
declare tmpVersion number;
BEGIN
select myPackage.GETPROJECTVERSION( :OLD.project_ID ) into tmpVersion from dual;
INSERT INTO mySchema.projectHistiry
( project_ID, ..., version )
VALUES
( :OLD.project_ID,
...
tmpVersion
);
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END ;
/
I got three triggers for each of my tables (projects, tasks, clients).
Here is the challenge: Not everything changes at the same time. For example, somebody could just update a certain tasks' cost. In this case, only one trigger fires and I got one insert. I'd like to insert one record into 3 history tables at once even if nothing changed in the projects and clients tables.
Also, what if somebody changes a project's end_date, the cost, and say the picks another client. Now, I have three triggers firing at the same time. Only in this case, I will have one record inserted into my three history tables. (which I want)
If i modify the triggers to do insert into 3 tables for the first example, then I will have 9 inserts when the second example happens.
Not quite sure how to tackle this. any help?
To me it sounds as if you want a transaction-level snapshot of the three tables created whenever you make a change to any of those tables.
Have a row level trigger on each of the three tables that calls a single packaged procedure with the project id and optionally client / task id.
The packaged procedure inserts into all three history tables the relevant project, client and tasks where there isn't already a history record for that key and transaction (ie you don't want duplicates). You got a couple of choices when it comes to the latter. You can use a unique constraint and either a BULK select and insert with FORALL/SAVE EXCEPTIONS, DML error logging (EXCEPTIONS INTO) or a INSERT...SELECT...WHERE NOT EXISTS...
You do need to keep track of your transactions. I'm guessing this is what you were doing with myPackage.GETPROJECTVERSION. The trick here is to only increment versions when you have a new transaction. If, when you get a new version number, you hold it in a pacakge level variable, you can easily tell whether your session has already got a version number or not.
If your session is going to run multiple transaction, you'll need to 'clear' out the session-level version number if it was part of a previous transaction. If you get DBMS_TRANSACTION.LOCAL_TRANSACTION_ID and store that at the package/session level as well, you can determine if you are in a new transaction, or part of the same transaction.
From your description, it looks like you would be capturing the effective and end date for each of the history rows once any of the original rows change.
Eg. Project_hist table would have eff_date and exp_date which has the start and end date for a given project. Project table would just have an effective date. (as it is the active project).
I don't see why you want to insert rows for all three history tables when only one of the table values is updated. You can pretty much get the details as you need (as of a given date) using your current logic. (inserting old row in the history table for the table that has been updated only.).
Alternative answer.
Have a look at Total Recall / Flashback Archive
You can set the retention to 10 years, and use a simple AS OF TIMESTAMP to get the data as of any particular timestamp.
Not sure on performance though. It may be easier to have a daily or weekly retention and then a separate scheduled job that picks out the older versions using the VERSIONS BETWEEN syntax and stores them in your history table.

Resources