T-SQL and transaction flow - from first to last - sql

Let's say I have table TabA with columns:
col1 - primary key (but not identity)
col2 - foreign key
col3 - with unique constraint
col4 - with check constraint
col5 - with NOT NULL constraint
Also, TabA has 2 triggers:
INSTEAD OF INSERT - this one cancel insert into TabA (of course), but in it's own code insert new row into TabA. The values for all column in this new row are guaranteed correct
AFTER INSERT - this one just print string
Now, I am ready insert new row into TabA (INSERT INTO TabA VALUES(...)). Obviously, we have to expect some events:
value for col1 must be checked for uniqueness and NOT NULL(primary key)
value for col2 must be checked for conformity to the parental table(foreign key)
value for col3 must be checked for uniqueness
value for col4 must be checked against check constraint
value for col5 must be checked for NOT NULL
INSTEAD OF trigger must be executed
AFTER trigger must be executed
What I want is reorder this list(1-7) so that number 1 be on event that will happen first, 2=event that will happen second, ..., and 7 for the last event.
Also, if event X produce error (col5=NULL, for example) - does this mean that events X+1,X+2.. will NOT happen?
Thanks for you help!

This is easy to test by setting up test tables as described with print statements in the triggers and simply trying to insert invalid values. Doing so gave for me
Instead Of Trigger
Checks NULL of PK
Checks NULL of column 5
Checks uniqueness of PK constraint
Checks uniqueness of unique constraint
Checks check constraint of column 4
Checks FK constraint
Fires After Trigger
As far as I know the order of 1,7, and 8 are guaranteed. The rest are arbitrary. Any error will stop succeeding steps.

General order of execution for INSERT, UPDATE, DELETE statements:
Enforce all table- and row-level constraints. Note that you have zero control over the order in which these constraints are checked.
If an INSTEAD OF trigger exists for the statement being executed, execute it.
Execute all apropriate AFTER triggers, in undefined order, with the following exceptions:
IF an after trigger has been specified as the first or last to be executed via sp_settriggerorder, execute those at the appropriate point, with any remaining triggers executed in undefined order.
You may have only 1 INSTEAD OF trigger (per action: INSERT, UPDATE or DELETE). That trigger will always get executed before any AFTER triggers, since it executes in lieue of the corresponding INSERT, UPDATE or DELETE statement.
AFTER triggers are always executed, oddly enough, AFTER the data modification statement executes.
NOTE: If you have an INSTEAD OF trigger, it is unclear to me, not having spent any real amount of time fussing with INSTEAD OF triggers, whether or not table/row constraints are enforced prior to execution of the INSTEAD OF trigger. That's something you might want to experiment with.

This is easy to test by setting up test tables as described with print
statements in the triggers and simply trying to insert invalid values.
Of course, I did it! And agree with you - get the same result from 1 to 8. And what is embarrass for me is the quote from "Microsoft SQL Server 2008 Bible" book (by Paul Nielsen). That is (on page 637):
Every transaction moves through the various checks and code in the
following order:
IDENTITY INSERT check
Nullability constraint
Data-type check
INSTEAD OF trigger execution. If an INSTEAD OF trigger exists, then
execution of the DML stops here. INSTEAD OF triggers are not
recursive. Therefore, if the INSERT trigger executes another DML
command, then the INSTEAD OF trigger will be ignored the second time
around (recursive triggers are covered later in this chapter).
Primary-key constraint
Check constraints
Foreign-key constraint
DML execution and update to the transaction log
AFTER trigger execution
Commit transaction
So, as you see, this IT-Pro disagree with you and me. He, for example, give INSTEAD OF trigger number 4 whereas we give to it number 1. This quote just baffle me!

Related

SQL integrity checks different on update and on insert

I have a SQL CHECK CONSTRAINT which checks values base on a column value (located on a different TABLE -> so FK is not an option).
Basically something like:
--Function
CREATE FUNCTION [dbo].[func_CHECK_ID_EXISTS]
(
if ...
return 1
else return 0
)
--CHECK CONSTRAINT
ALTER TABLE [dbo].[MY_TABLE]
ADD CONSTRAINT CHECK_ID_EXISTS
CHECK ([dbo].[func_CHECK_ID_EXISTS](MyColumn)=1);
This works perfectly.
Now I need this to evolve.
On an update, I would need to make the same integrity check than before (based on MyTable2.A).
But on an insert, I need to make an integrity check based on another column (based on MyTable2.B).
As far as I know, neither the SQL function func_CHECK_ID_EXISTS neither the constraint CHECK_ID_Eenter code hereXISTS knows if the element is being inserted or updated right?
So the check constraint does not seem to be an option anymore...
I had in mind to make an alternate solution with an update trigger and an insert trigger. Indeed I can set different conditions into each trigger.
But although the insert trigger can check the inserted element via the INSERTED pseudo table, I think I do not have this possibility for the UPDATE trigger.
I mean there is no UPDATED pseudo table is this correct?
So is there a possibility to compare the columns of the updated element within the update trigger?

Get a reference to insert rows with triggers

I'm trying to get a reference to a set of rows that I'm trying to insert into a table through a multiple insert. For example if I execute:
INSERT INTO T VALUES (0,'A'),(1,'B'),(2,'C')
I would like to get a reference in a before insert trigger to a "table" that contains these 3 rows. Is that possible?
And another question: what does a REFERENCING NEW_TABLE represents in a before trigger (maybe could this be the answer to the first question)?
Thanks
According to documentation:
REFERENCING NEW TABLE AS identifier
Specifies a temporary table name which identifies the affected rows as modified by the triggering SQL operation and by any SET
statement in a BEFORE trigger that has already executed.
Also take a look:
FOR EACH STATEMENT
Specifies that the triggered action is to be applied only once for the whole statement. This type of trigger granularity cannot be
specified for a BEFORE trigger or an INSTEAD OF trigger (SQLSTATE
42613). If specified, an UPDATE or DELETE trigger is activated, even
if no rows are affected by the triggering UPDATE or DELETE statement.
maybe it will suite better your needs (of course you need to go with AFTER trigger)

get Primary Key from SP

With SQL, when inserting values into a Table from a SP, is it possible to get the value of the Primary Key before the values are added to the Table?
This is certainly possible, leveraging the power of relational databases. Assuming, like Martin Smith said, that you are using an autogenerated key, then you can use a transaction to do what you're looking for.
Here's the general idea:
Start a transaction.
Do the insertion.
Use the primary key to do whatever you need to do, including updated the inserted row to reflect the value.
Commit or rollback
By beginning a transaction before your insert, you can assure that no one else will be able to see the new rows until you commit your transaction. If the key is not to your liking, you can rollback the transaction, and no one else will know. If the key is adequate, you can modify the rows you have just inserted before committing.
Since you have a transaction, no one else can see the intermediate data you have inserted before you commit. Thus, you can update the rows that you have just inserted as if you have the primary key before your actual insert.
If you are using SQL Server the best way to do this is with your MERGE or INSERT command use the OUTPUT clause to get your key back. Even though this is not before the insert you can use the results of the OUTPUT to join back the results of your data to insert subsequent children records.
Also if you are using SQL server you can look at IDENT_CURRENT function which will return the current identity value of a table. If you are writing your SQL in a set based fashion the OUTPUT that I mention above works best for me.

sql error - unique constraint

I have one data migration script like this.
Data_migration.sql
It's contents are
insert into table1 select * from old_schema.table1;
commit;
insert into table2 select * from old_schema.table2;
commit;
And table1 has the pk_productname constraint when I execute the script
SQL> # "data_migration.sql"
I will get an unique constraint(pk_productname) violation. But when I execute the individual sql statements I won't get any error. Any reason behind this. And how to resolve this.
The failure of the unique constraint means you are attempting to insert one of more records whose primary key columns collide.
If it happens when you run a script but not when you run the individual statements then there must be a bug in your script. Without seeing the script it is impossible for us to be sure what that bug is, but the most likely thing is you are somehow running the same statement twice.
Another possible cause is that the constraint is deferred. This means it is not enforced until the end of the transaction. So the INSERT statement would appear to succeed if you run it without issuing the subsequent COMMIT.
It is common to run data migration without enabled constraints. Re-enable them afterwards using an EXCEPTIONS table. This makes it easier to investigate problems. Find out more.

What kind of errors exists in SQL querys for ROLLBACK?

For example:
insert into table( a, b ) values ('a','b') could generate the following error:
**a-b duplicate entry**
BUT here I can ignore this error selecting the ID of this values, then use this ID:
select ID from table where a = 'a' and b = 'b'
insert into brother( table ) values (ID)
Finally I could COMMIT the PROCEDURE. Look that this error isn't relevant for rollback if I need the ID.
The question is: what kind of errors will doing me to ROLLBACK the PROCEDURE???
I hope you understand.
I think you're asking, "What kind of errors can an INSERT statement cause that will make MySQL rollback a transaction?"
An INSERT that violates any constraint will cause a rollback. It could be foreign key constraint like you've outlined, but it could also be a UNIQUE constraint, or a CHECK constraint. (A CHECK constraint would probably be implemented as a trigger in MySQL.)
Trying to insert values that aren't valid (NULL in nonnullable columns, numbers that are out of range, invalid dates) might cause a rollback. But they might not, depending on the server configuration. (See link below.)
An INSERT can also fail due because it lacks permissions. That will also cause a rollback.
Some conditions that would cause a rollback on other platforms don't cause a rollback on MySQL.
The options MySQL has when an error
occurs are to stop the statement in
the middle or to recover as well as
possible from the problem and
continue. By default, the server
follows the latter course. This means,
for example, that the server may
coerce illegal values to the closest
legal values.
That quote is from How MySQL Deals with Constraints.
One of my favorite quotes from the MySQL documentation, 1.8.6.2. Constraints on Invalid Data.
MySQL enables you to store certain
incorrect date values into DATE and
DATETIME columns (such as '2000-02-31'
or '2000-02-00'). The idea is that it
is not the job of the SQL server to
validate dates.
Isn't that cute?