Question

I need some help about triggers. I’m currently developing a platform and a database in order to manage exams at my university. Here is my problem:

I have 1 supertype table, which contains all the persons registered on the platform. I have to be able to make the distinction for each person between the functions “Candidate” and “Examiner”. So I have my 2 subtype tables, one for all the candidates and one for all the examiners. To achieve that, I’m using insert triggers.

In addition, a person can be both Candidate and Examiner, but not at the same time. So after updating the supertype table, I also need a trigger to be able to delete a specific row from one of the two-subtype table and insert the user information on the other.

Here is a simplified design of these 3 tables:

enter image description here

My INSERT trigger :

ALTER TRIGGER [dbo].[role_insert]   
ON [dbo].[alemp_persons]  
FOR INSERT  
AS
  DECLARE @random_number int
  SELECT @random_number = CAST(CAST(rand() as binary(2)) as int)
BEGIN
  INSERT INTO dbo.alemp_candidates 
  ( 
  id_person, random_number
  )  
  SELECT id_person, @ random_number
  FROM INSERTED  
  WHERE function='Candidate'

  INSERT INTO dbo.alemp_examiners
  (  
  id_person
  )  
  SELECT id_person
  FROM INSERTED  
  Where function='Examiner'
END
GO

My UPDATE trigger :

ALTER TRIGGER [dbo].[role_update] ON [dbo].[alemp_persons]
AFTER UPDATE
AS
  DECLARE @id_person int 
  DECLARE @newFunction int SELECT @newFunction=function FROM inserted
  DECLARE @random_number int SELECT @ random_number = CAST(CAST(rand() as binary(2)) as int)

IF @newFunction = 'Candidate'
BEGIN
  DELETE
  FROM dbo.alemp_examiners
  WHERE id_person=@id_person
END

BEGIN
  SET IDENTITY_INSERT dbo.alemp_candidates ON;
  INSERT INTO dbo.alemp_candidates  
  (  
  id_person, random_number
  )  
  SELECT  @id_person, random_number
  SET IDENTITY_INSERT dbo.alemp_candidates OFF;
END

IF @newFunction = 'Examiner'
BEGIN
  DELETE 
  FROM dbo.alemp_candidates
  WHERE id_person=@id_person
END

BEGIN
  SET IDENTITY_INSERT dbo.alemp_examiners ON;
  INSERT INTO dbo.alemp_examiners
  (  
  id_person
  ) 
  SELECT  @id_person
  SET IDENTITY_INSERT dbo.alemp_examiners Off;
END
GO

As I said above, my INSERT trigger works as I want. However when I want to update the function of one person, I got an error :

Explicit value must be specified for identity column either when IDENTITY_INSERT is set to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.

Was it helpful?

Solution

Some simple notes:

1) You should follow Mitch Wheat's advice and rewrite these triggers because inserted and deleted tables could have more than one row. For example, your trigger will have a bad behavior when is executed the next statement UPDATE [dbo].[alemp_persons] SET function = CASE WHEN id_person = 1 THEN 'Candidate' ELSE 'Examiner' END WHERE id_person IN (1,2) if the first person's function is 'Examiner' and the second person's function is 'Candidate'.

2) [dbo].[alemp_persons].function's data type should be [tiny]int or char(1) and not varchar(something greater than 1) (Where function='Examiner').

3) [dbo].[alemp_persons].function column should disallow Nulls.

4) [dbo].[alemp_persons].function column should has a CHECK constraint:

ALTER TABLE [dbo].[alemp_persons] ADD CONSTRAINT CK_alemp_persons_function_Verify CHECK ( function IN ('Candidate', 'Examiner') );

5) It would be nice to add

  • a function column to [dbo].[alemp_candidates] and [dbo].[alemp_examiners] tables,

  • two check constraints on [dbo].[alemp_candidates] (function = 'Candidate') and [dbo].[alemp_examiners] (function = 'Examiner'),

  • an UNIQUE index on [dbo].[alemp_persons](id_person, function),

  • two FKs between [dbo].[alemp_candidates/examiners](id_person, function) and [dbo].[alemp_persons](id_person, function).

This way, you can be sure that [dbo].[alemp_candidates] table has only candidates and [dbo].[alemp_examiners] has only examiners and every person can be only candidate or examiner at one time.

6) You should disallow IDENTITY property for id_person columns in [dbo].[alemp_candidates] and [dbo].[alemp_examiners] table (SET IDENTITY_INSERT dbo.alemp_candidates ...).

8) And this statement IF @newFunction = 'Candidate' should raise an error because @newFunction data type is 'INT'.

9) And the AFTER UPDATE trigger on [dbo].[alemp_persons] table will move data between candidates and examiners tables (not tested):

ALTER TRIGGER [dbo].[role_update]   
ON [dbo].[alemp_persons]  
FOR UPDATE
AS
BEGIN

DECLARE @selected_rows TABLE (
id_person INT PRIMARY KEY, -- or BIGINT, look at alemp_person.id_person data type 
new_function VARCHAR(50) NOT NULL -- look at alemp_person.function column data type 
);

INSERT @selected_rows (id_person, new_function)
SELECT new.id_person, new.function
FROM inserted as new INNER JOIN deleted as old ON new.id_person = old.id_person
WHERE new.function <> old.function;

MERGER dbo.alemp_candidates AS dest
USING @selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN 
    DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Candidate' THEN
    INSERT (id_person, random_number)
    VALUES (src.id_person, CONVERT(BINARY(2), CHECKSUM(NEWID()));

MERGER dbo.alemp_examiners AS dest
USING @selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN 
    DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Examiner' THEN
    INSERT (id_person)
    VALUES (src.id_person);
END
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top