Reputation: 2652
I need to replace a unique constraint with a trigger, but I found a problem (I'm using DB2). Let's say I have a table R with a attribute A integer. I defined this trigger (similar for update):
CREATE TRIGGER R_A_UNIQUE
BEFORE INSERT ON R
REFERENCING NEW AS N
FOR EACH ROW
WHEN (EXISTS(SELECT * FROM R WHERE A = N.A))
SIGNAL SQLSTATE '70000' ('ATTRIBUTE A MUST BE UNIQUE');
It works in normal cases, but if add multiple rows like that:
INSERT INTO R VALUES (4),(4),(4);
all values are inserted with no problem!
Am I missing something? Thank you very much
Upvotes: 4
Views: 2359
Reputation: 6496
You stated in comments above that the reason for using a trigger instead of unique constraint is that "In DB2 you can't define an attribute as unique if it allows null values".
According to what I have read, DB2 will allow a "unique index" on Nullable columns, but will not allow "unique constraints" on nullable columns.
Another issue is that DB2 by default will only allow one NULL
value within a unique index on a single column, and will treat 2 NULL
values as a violation of the unique index.
You can work around this issue, and still use a unique index, by using Generated columns for the columns that allow NULL
s, using the pattern mentioned in this article (https://www.ibm.com/developerworks/community/blogs/SQLTips4DB2LUW/entry/unique_where_not_null_indexes26?lang=en), which states
What is needed is an extra column that makes all NULL keys unique but does not interfere with uniqueness enforcement if there is any NULL column in the key
Steps:
1) Alter table R
to add a generated column which will make the NULL
values be treated as unique within the index.
ALTER TABLE R
ADD A_UNQKEY INT GENERATED ALWAYS AS (CASE WHEN A IS NULL THEN pk ELSE NULL END))
...where pk
is the primay key of table r
. Also note that you may need to change the data type from INT
to whatever type your primary key is.
2) Create the unique index on both a
and a_unqkey
columns.
CREATE UNIQUE INDEX UIX_R_A ON R(a, a_unqkey);
So now, an example of values in table R
:
pk, a, a_unqkey
1, NULL, 1
2, 'value1', NULL
3, 'value2', NULL
4, NULL, 4
Without the a_unqkey
field, your unique index on column a
would look like this:
NULL
'value1'
'value2'
NULL <<**VIOLATES UNIQUE INDEX b/c DUPLICATE OF NULL FROM ROW 1**
However, if you create the unique index on R(a, a_unqkey)
, then it would use the following values:
NULL, 1
'value1', NULL
'value2', NULL
NULL, 4 <<Still unique b/c the other pair is NULL, 1
Upvotes: 0
Reputation: 1269445
This is too long for a comment.
The insert
is a single transaction. So, the state of the table does not change during the course of the inserts. That is, the trigger is called but on the original version of the table, all three times.
This makes it difficult to implement something like a unique constraint inside a trigger, and one that can handle multiple inserts at the same time. My advice is to stick with the unique constraint and not try to reproduce it in a trigger.
I should add that you might be able to change the semantics of the trigger by using an "after insert" trigger. However, this would allow the duplicates into the table, and then you would have to delete them afterwards.
Upvotes: 2