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?
Related
The problem here is that, we use an application called ArcGIS that creates an upper management layer for our database, and when our users use ArcGIS to create a new object in the database, it adds a default value (0) to the primary key, so when the second object is created it triggers an ORA error for having duplicate values.
So my idea was to create an auto increment trigger for our PK but use AFTER INSERT instead.
I couldn't find any example online for this specific case and simply switching BEFORE to AFTER gives an error saying that you can't use NEW with AFTER
SQL code of what I tried (taken from other questions):
CREATE OR REPLACE TRIGGER "IMOVEL_TRIGGER"
after insert on IMOVEL
for each row
begin
select IMOVEL_SEQ.nextval into :NEW.GEOCODIGO_IMOVEL from dual;
end;
It cant be a BEFORE INSERT trigger, because the application overwrites it
Simplifying, what I need is a AFTER INSERT trigger, that updates the PK to the sequence .nextval, it doesn't let me use :OLD or :NEW, so I'm not sure what must be done.
Or an update trigger that only runs after it is created
This is pretty new territory for me, having to learn SQL now just to solve this issue
You can change :NEW values only in a BEFORE trigger. By the time you reach the AFTER trigger, the row has already been inserted, so it's too late to change the columns.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7004.htm says:
Restrictions on BEFORE Triggers
BEFORE triggers are subject to the following restrictions:
You cannot specify a BEFORE trigger on a view or an object view.
You can write to the :NEW value but not to the :OLD value.
Restrictions on AFTER Triggers
AFTER triggers are subject to the following restrictions:
You cannot specify an AFTER trigger on a view or an object view.
You cannot write either the :OLD or the :NEW value.
It's not clear why you want to use an AFTER trigger for assigning sequence values to the PK. The common solution is to use a BEFORE trigger.
See example in: How to create id with AUTO_INCREMENT on Oracle?
CREATE SEQUENCE IMOVEL_TRIGGER_SEQ START WITH 1;
Trigger definition:
CREATE OR REPLACE TRIGGER IMOVEL_TRIGGER
BEFORE INSERT ON IMOVEL
FOR EACH ROW
BEGIN
SELECT IMOVEL_TRIGGER_SEQ.NEXTVAL
INTO :new.GEOCODIGO_IMOVEL
FROM dual;
END;
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)
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.
I have an issue related to data in sql server. In my database some of the constraint were not enabled i.e. they were not checked , After some time working on it we found this issue that a parent rows can be deleted without deleting child, which was an issue. I enabled all the constraint in the database using query
ALTER TABLE tbl_name CHECK CONSTRAINT ALL
above query was executed on all the tables of that database without any error . But my concern is whether it will work or not , if it will work on the existing data then what will happen to that data whose parent table data has been deleted.
I want to know is there any way such that I can validate such data data whose parent record doesn't exist in the entire database. There are about 270 constraint containing FOREIGN KEY AND UNIQUE KEY . I don't want to go for manual option.
Please help me out.
ALTER TABLE tbl_name CHECK CONSTRAINT ALL
only re-enables the constraints. Importantly, the constraints are not checked against the existing data in the database (nor are they trusted by the optimizer). If you want that to occur, you need to specify WITH CHECK as well:
ALTER TABLE tbl_name WITH CHECK CHECK CONSTRAINT ALL
(And yes, the word CHECK appears twice)
If you execute this, and there are orphaned child rows (or other invalid constraints), then the ALTER TABLE will fail with an error message. There's nothing SQL Server can do to fix this issue - it's for you to decide whether to a) remove the orphaned rows, or b) to re-create, in some manner, a suitable parent row for them.
You can also add the 'ON DELETE CASCADE' code to the end of foreign keys to prevent orphaned child rows from persisting.
This is more of a 'better practice' going forward than a solution, but I believe Damien_The_Unbeliever has answered your main question.
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!