Order of execution of trigger and statements in Oracle stored procedure - oracle11g

Below are my table structures :
Table -Customer
CustomerID Blacklisted Customer Name
101 Y ABC
102 Y DEF
Table -Blacklist
CustomerID BlacklistID Customer Name
101 1011 ABC
102 1012 DEF
Table -Reason
BlacklistID ReasonID Reason Code
1012 02 Rcode2
Main table "Customer" is to store customer information.There is a trigger after update on table "Customer" to insert record in table "Blacklist" if somebody updates the blacklisted as Y in customer table.
We consider the customer as blacklisted if ,
Blacklisted column in Customer table as value 'Y' and.
There are records present for customer in Blacklist and Reason table
Now my requirement is to blacklist the customer from backend.For this i am writing stored procedure with below queries:
Update customer set blacklisted ='Y' where customerid='102';
select BlacklistID into var_id from blacklist where customerid='102';
Insert into reason(BlacklistID,ReasonID,ReasonCode)values(var_ id,111,'RCODE1');
Now to insert entry in Reason table(step-3),i need BlacklistID which is a foreign key and i will get the value of BlacklistID once the trigger on customer table gets exceuted.So my confusion is, can i assume the trigger on update of 'Customer' table will always get excuted before the cntrl reaches my INSERT INTO reason(step-3) statement. Please suggest.

If you need to be certain about the order of trigger execution, you can specify this order when creating the trigger.
This is done with the FOLLOWS ... and PRECEEDS ... options of the create trigger statement:
More details in the manual: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/create_trigger.htm#CJAEJAFB
FOLLOWS | PRECEDES
Specifies the relative firing of triggers that have the same timing point. It is especially useful when creating crossedition triggers, which must fire in a specific order to achieve their purpose.
Use FOLLOWS to indicate that the trigger being created must fire after the specified triggers. You can specify FOLLOWS for a conventional trigger or for a forward crossedition trigger.
Use PRECEDES to indicate that the trigger being created must fire before the specified triggers. You can specify PRECEDES only for a reverse crossedition trigger.

Yes. Triggers are part of the statement. Although you cannot be fully certain *) of the order in which multiple triggers in the same statement are executed, you can be certain that they al are done when the statement itself is done. So by the time of step 2, all update triggers of step one have fired.
*) Actually, the default order is:
Statement level before triggers
Row level before triggers
Row level after triggers
Statement level after triggers
But if you have, say, two row level before trigger, by default you cannot be certain in which order those two are executed. But I learned from the comments that in Oracle 11, you can actually specify the order to cover even those cases.

I don't see a need for all these tables, and therefore no need for a trigger.
Why do you not just use a blacklist reason code in the customer table? The purpose of the blacklist table is unclear as it seems to add no data and just repeats data from the customer table with an additional id column?
Or if you need multiple reason codes then just use a blacklist_reason table that references the customer id and a reason code -- I don't think you even need the blacklisted column in the customer table.

Related

For Each ,For First

What is the meaning of For each and For First.. Example below
FOR EACH <db> NO-LOCK,
FIRST <db> OF <db> NO-LOCK:
DISPLAY ..
Also why we need to use NO-LOCK for every table for every time.
Let's answer by giving an example based on the Progress demo DB:
FOR EACH Customer WHERE Customer.Country = "USA" NO-LOCK,
FIRST Salesrep WHERE Salesrep.salesrep = Customer.Saleserp:
/* your code block */
END.
The FOR EACH Block is an iterating block (loop) that integrates data access (and a few more features like error handling and frame scoping if you want to go that far back).
So the code in "your code block" is executed for every Customer record matching the criteria and it also fetches the matching Salesrep records. The join between Customer and Salesrep is an inner join. So you'll only be processing Customers where the Salesrep exists as well.
FOR statement documentation (includes EACH and FIRST keywords)
NO-LOCK documentation
Google is your friend and documentation on packages is usually quite user-friendly.
Try not to ask questions that can be solved by simple search on StackOverflow.
FOR EACH table
Selects a set of records and starts a block to process those records.
NO-LOCK means what it says, the records are retrieved from the database without any record locking. So you might get a "dirty read" (uncommitted data) and someone else might change the data while you are looking at that record.
That sounds awful but, in reality, NO-LOCK reads are almost always what you want to use. If you do need to update a NO-LOCK record you can just FIND CURRENT with a lock.
FOR EACH NO-LOCK can return large numbers of records in a single network message whereas the other lock types are one record at a time - this makes NO-LOCK quite a bit faster for many purposes. And even without the performance argument you probably don't want to be taking out large numbers of locks and preventing other users running inquiries all the time.
Your example lacks a WHERE clause so, by default, every record in the table is returned using the primary index. If you specify a WHERE clause you will potentially only have a subset of the data to loop through and the index selection may be impacted. You can also add a lot of other options like BY to specify sort order.
FOR FIRST is somewhat similar to FOR EACH except that you only return, at most, a single record. Even if the WHERE clause is empty or would otherwise specify a larger result set. BUT BE CAREFUL - the "FIRST" is deceptive. Even if you specify a sort order using BY the rule is "selection, then sorting". At most only one record gets selected so the BY doesn't matter. The index dictated by the WHERE (or lack of a WHERE) determines the sort order. So if your request something like:
FOR FIRST customer NO-LOCK BY discount:
DISPLAY custNum name discount.
END.
You will fetch customer #1, not customer #41 as you might have expected. (Try the code above with the sports2000 database. Replace FIRST with EACH in a second run.)
FOR EACH table1 NO-LOCK,
FIRST table2 NO-LOCK OF table1:
or
FOR EACH customer NO-LOCK,
FIRST salesRep NO-LOCK OF customer:
DISPLAY custnum name customer.salesRep.
END.
Is a join. The OF is a shortcut telling the compiler to find fields that the two tables have in common to build an implied WHERE clause from. This is one of those "makes a nice demo" features that you don't want to use in real code. It obfuscates the relationship between the tables and makes your code much harder to follow. Don't do that. Instead write out the complete WHERE clause. Perhaps like this:
for each customer no-lock,
first salesRep no-lock where sakesRep.salesRep = customer.salesRep:
display custnum name customer.salesRep.
end.

how to insert into 2 table using join in sql server?

I have 2 Tables :
T_Customer
T_Address
In T_Customer Table have T_Address Foreign Key.
on Web Page Created form Where Customer can add there Details like First Name, Last Name, Address etc.
When Customer Submit form then Adddress details should insert in T_Address table And T_Address ID goes To T_Customer Table is it possible to Create 2 insert operations on one button click?
Is Joins is possible in this?
A single INSERT statement can only affect one table, albeit that you might have some triggers that in turn affect other tables. However, your question was specifically about an action on one button click. This is very possible, and indeed highly advisable. Simply create yourself a single stored procedure and pass it as parameters all the fields required for both Customer and Address tables.
Then within your Stored procedure you can have two inserts, one to Customers and one to Address. If you have a referential integrity constraint, then you must insert customers before address.
The advantage of this approach, is that all commands within a stored procedure are covered by the same transaction: it all succeeds or all fails, and requires only one round trip to the database, in turn being quicker and using less resources.

Increment in Oracle Forms without commit record

I have used this code to generating an auto number:
DECLARE
ACC_NEW_ID NUMBER:=0;
BEGIN
if :acc_info_id is null then
SELECT MAX(NVL(ACC_INFO_ID,1000))+1 INTO ACC_NEW_ID FROM ACC_INFO;
:ACC_INFO_ID := ACC_NEW_ID;
end if;
END;
This code is working perfectly, but when I create one further record without clicking save button it creates the same number. For example: id is 1003 and after posting the record, I clicked new record button instead of save button, It generates the same 1003 number instead of 1004 as I expected.
Your code queries the records in the table, and returns the maximum ID that it can see at that point in time (i.e. it won't see uncommitted records), and adds one to it.
If it generates 1003 but no record with this value is saved into the table, of course you should expect it to not find it!
As far as that is concerned, it "works perfectly" and correctly. If, however, the intention is to generate a unique ID value for each record, this approach is flawed and will fail in any normal system due to concurrency.
Instead, if you need a unique ID you should use an Oracle SEQUENCE instead, which guarantees uniqueness.

SQLite Triggers: Only have a trigger take an action when a certain field is NOT included in the query?

As is common, I've got a trigger that updates a date_last_modified field in one of my tables. The issue I've run into is sometimes I need to manually adjust the timestamp in that date_last_modified field. When I do, though, the trigger fires and sets it right back to now().
Here is my trigger:
CREATE TRIGGER Preference_on_update After UPDATE ON Preference for each row
begin
update Preference set date_last_modified = datetime('now')
where rowid = old.rowid;
end
What I'd like to do is ONLY have this trigger fire when the update does not already include a value for the date_last_modified field. If the update statement included date_last_modified, the trigger should not adjust anything.
I've looked through the docs and syntax for triggers for SQLite, and see there is a "WHEN" clause, but none of my incantations of it so far have been fruitful.
Can this be done?
Triggers can be restricted to a specific set of columns:
CREATE TRIGGER Preference_on_update
AFTER UPDATE OF ID, Name, Value, Description ON Preference
FOR EACH ROW
BEGIN
...
However, this does not help when the date is changed together with any of these other columns.
A better idea is to check whether the date value was actually changed, i.e., whether the old and new values are different.
(<> is always false when NULL values are involved, so it's better to use IS NOT NULL here.)
CREATE TRIGGER Preference_on_update
AFTER UPDATE ON Preference
FOR EACH ROW
WHEN OLD.date_last_modified IS NOT NEW.date_last_modified
BEGIN
...

How to assign an ID but then delete if not used

I am unsure on how to do this 'best practice' wise.
I have a web application (asp.net VB) that connects to an MS SQL server 2012. Currently when the page loads the app connects to a DB table and gets the last ID and adds 1 to it and displays this to the user. When the user submits the form the new ID is saved to the DB.
The problem being the app may be opened by 2 users at the same time and therefore they will get assigned the same ref number which will cause problems when the data is saved.
How can I assign different numbers to different users if the app is opened at the same time without saving unnecessary data?
You have multiple solutions for this, I'll try to outline a few approaches. (I'll assume that you need to insert things into a DB that I'll call "orders".)
First of all, you can move the ID-generation to the moment when the order is actually inserted, and not at the moment when the user start to enter the data. That way, you do not generate an ID for a user that never completes the form. Also this scenario is easy to accomplish using autoincrementing values in sql server. You can, for example do:
-- create a table with an identity column
create table Orders (
ID int identity(1,1) not null,
Description nvarchar(max) not null
);
-- insert values, without specifying the ID column
insert into Orders (Description) values ()
-- select the row back
-- returns 1, 'My First Order'
select * from Orders;
Another way to do this is to use SQL Server Sequences. These are things that do nothing except generate numbers in a row. They guarantee that the numbers won't be repeated, and always keep count of the current value, i.e.
-- create a sequence
create sequence OrderIdSequence
start with 1
increment by 1;
-- get the next sequence value
select next value for OrderIdSequence

Resources