Create trigger for "before insert" - sql-server

I want to make a trigger for one table that will be used before INSERT.
I want to check if two columns are NULL and if they are NULL raise an error otherwise INSERT the data.
So far I have this:
CREATE TRIGGER INS_TABLE_1
ON mytable
FOR INSERT
AS
BEGIN
IF (column1 IS NULL OR column2 IS NULL)
BEGIN
RAISERROR ('You are not allowed to Add These Data.', 10, 11)
END
ELSE
INSERT INTO mytable (column1,column2,column3)
END
Can you please help me?

Use instead trigger and inserted table like below and have a try.
CREATE TRIGGER INS_TABLE_1
ON mytable
INSTEAD OF INSERT
AS
BEGIN
DECLARE #fn varchar(50),#ln varchar(50)
SELECT #fn=column1 ,#ln=column12 from inserted
IF (#fn IS NULL OR #ln IS NULL)
BEGIN
RAISERROR ('You are not allowed to Add These Data.', 10, 11)
END
ELSE
INSERT INTO mytable (column1 ,column2) values (#fn,#ln)
END
The inserted table stores copies of the affected rows during INSERT and UPDATEstatements. Instead of trigger replaces the current INSERT by the trigger definition.

Related

What is the correct way of using ##IDENTITY

Question regarding the ##IDENTITY, I have 4 different tables:
Customer [Id]
Person [Id, fname, lname]
Account [Cd, owner, balance]
Transaction [Id, account, type]
Customer Id has a feature of identity increment 1.
My goal is to create a new person for the database, so that
Customer.Id = Person.Id = Account.owner = Transaction.ID
I have tried the following below, however I get this error:
Cannot insert null value into column owner
How do I correct the mistakes to make it work?
BEGIN TRAN
BEGIN TRY
INSERT INTO bank.customer DEFAULT VALUES
INSERT INTO bank.person (id, fname, lname)
VALUES (##IDENTITY, 'Mike', 'Phelps')
INSERT INTO bank.account (cd, owner, balance)
VALUES (2, ##IDENTITY, 0)
INSERT INTO bank.transaction (id, account, type)
VALUES (##IDENTITY, (SELECT cd FROM pankki.tili,'P')
END TRY
BEGIN CATCH
ROLLBACK
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
I suspect what you want is this:
BEGIN TRY
BEGIN TRAN;
DECLARE #ID int; --bigint, decimal?
INSERT INTO bank.customer DEFAULT VALUES;
SET #ID = SCOPE_IDENTITY(); --Assumes customer has a column with an IDENTITY
INSERT INTO bank.person (id,fname,lname)
VALUES(#ID,'Mike','Phelps');
INSERT INTO bank.account (cd,owner,balance)
VALUES(2,#ID,0);
INSERT INTO bank.transaction(id,account,type)
SELECT #ID,
cd,
'P'
FROM pankki.tili; --I assume, therefore, that pankki.tili only ever has 1 row
COMMIT; --YOu were missing this
END TRY
BEGIN CATCH
ROLLBACK;
THROW; --Don't SELECT the error details, THROW it.
END CATCH
From the Microsoft document:
After an INSERT, SELECT INTO, or bulk copy statement is completed, ##IDENTITY contains the last identity value that is generated by the statement. If the statement did not affect any tables with identity columns, ##IDENTITY returns NULL.
I take it PERSON does not have an identity column, so when you insert into it, ##identity becomes NULL.
If you want to user the ##identity from the insert for the other tables, use it to set the value of a variable.
declare #PersistentID int;
INSERT INTO bank.customer DEFAULT VALUES
set #PersistentID = ##IDENTITY -- or scope_identity() is safer
INSERT INTO bank.person (id,fname,lname)
VALUES( #PersistentID ,'Mike','Phelps')
First, you need to understand the difference between this two commands:
##identity returns the last inserted identity value in ANY table in the current session, regardless of scope.
IDENT_CURRENT('table_or_view') returns the last inserted identity value for a GIVEN table.
So, for your case, you need to use the second one.
And your script would be something like this:
BEGIN TRAN
BEGIN TRY
INSERT INTO bank.customer DEFAULT VALUES
SET #customerID = (SELECT IDENT_CURRENT('bank.customer'))
INSERT INTO bank.person (id,fname,lname)
VALUES( #customerID,'Mike','Phelps')
INSERT INTO bank.account (cd,owner,balance)
VALUES(2,#customerID,0)
INSERT INTO bank.transaction(id,account,type)
VALUES(#customerID,(SELECT cd FROM pankki.tili,'P')
END TRY
This way you can guarantee that the same ID in inserted in the four tables.
If you are using ##identity this value is changing with every new insert.

Create trigger which would not allow to delete values in specific column?

I've a simple table like this:
MARK {IdMark Int, IdStudent Int, Value Int, Subject Varchar(10)}
and I would like to create trigger which would not allow to delete rows in that table but there should be possible to alter values in column "Value" unless it's NULL.
The code below does not work like I would like to at all:
CREATE TRIGGER delValue
ON mark
FOR INSERT, UPDATE, DELETE
AS
IF EXISTS(SELECT 1 FROM inserted i JOIN deleted d ON i.IdMark = d.IdMark WHERE i.IdMark IS NULL)
BEGIN
RAISERROR('You can't delete marks!', 16, 1)
ROLLBACK
END
Try the following:
You only need to check inserted for null values. And if there is nothing in inserted but something in deleted then its a delete.
Also, watch out when using single quotes in a message, you need to escape them (by repeating them).
CREATE TRIGGER delValue
ON mark
FOR INSERT, UPDATE, DELETE
AS
begin
-- If attempting to set to null, rollback
IF EXISTS (SELECT 1 FROM inserted WHERE IdMark IS NULL) BEGIN
RAISERROR('You can''t set marks to null!', 16, 1);
ROLLBACK;
END
-- If attempting to set to delete, rollback
-- There will never be anything in inserted for a delete
IF NOT EXISTS (SELECT 1 FROM inserted) and EXISTS (SELECT 1 FROM Deleted) BEGIN
RAISERROR('You can''t delete marks!', 16, 1);
ROLLBACK;
END
end

Why I can not insert or update valid data after Instead of Trigger has been fired?

Here is my simple test trigger transactions:
First, I designed a table:
CREATE TABLE T2_Score (
UserID INT Primary key,
Months INT,
Score INT
);
Then, create an instead of trigger to set the restractions to make sure the value of Months should be between 1 and 12.
CREATE TRIGGER T2_Score_Months_Restriction
ON T2_Score
INSTEAD OF UPDATE, INSERT
AS
IF ((SELECT Months FROM inserted) > 12)
BEGIN
PRINT ('Month must be between 1 and 12!')
ROLLBACK TRAN
END
But, the issue is I can not insert any valid values if the trigger has been fired once.
For example:
INSERT INTO T2_Score VALUES (11,15,18);
And inserted filed if I try the valid value(No warning notes, but file to insert value into table), for example:
INSERT INTO T2_Score VALUES (11,12,18);
Can someone explain why and how to modify my code? Thanks!!
Wouldn't it be much simpler to use a constraint?
CREATE TABLE T2_Score (
UserID INT Primary key,
Months INT,
Score INT,
CONSTRAINT CHK_MONTHS_1_12 CHECK (Months BETWEEN 1 AND 12)
);
That trigger will never insert any rows. It is an INSTEAD OF trigger that doesn't have any INSERT statement.
You need:
CREATE TRIGGER T2_Score_Months_Restriction
ON T2_Score
INSTEAD OF UPDATE, INSERT
AS
IF (SELECT MAX(Months) FROM inserted) > 12 BEGIN
PRINT ('Month must be between 1 and 12!') ;
ROLLBACK TRAN ;
END
ELSE BEGIN
INSERT INTO T2_Score SELECT * FROM inserted ;
END ;
Or perhaps:
CREATE TRIGGER T2_Score_Months_Restriction
ON T2_Score
INSTEAD OF UPDATE, INSERT
AS
INSERT INTO T2_Score
SELECT * FROM inserted WHERE Months BETWEEN 1 AND 12 ;
The second versions allows partial inserts.
INSERT INTO T2_Score VALUES ( 4, 4, 4 ), ( 13, 13, 13 );
-- With the first trigger, this will insert 0 rows.
-- With the second trigger, this will insert 1 row.
The instead of trigger does not work in MySQL. You can use a BEFORE INSERT and BEFORE update and abort the action:
CREATE TRIGGER T2_Score_Months_Restriction
ON T2_Score
BEFORE INSERT,UPDATE
AS
IF ((SELECT Months FROM inserted) > 12)
BEGIN
DECLARE msg VARCHAR(255);
IF (SomeTestToFail = "FAIL!") THEN
set msg = "Month must be between 1 and 12!";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
END IF;
END;
Your INSERT will be ecexuted atomically. It will succeed as a whole, or it will fail as a whole. You might be able to work around that by just deleting invalid values from inserted, but I don't recommend it.
It's a really bad idea to allow a SQL statement to partially succeed when part of it fails.

SQL trigger on update or delete

I have to have one single trigger that fires on either the UPDATE OR DELETE operations. I have the trigger working fine for when one certain column is updated. However, I need different logic for when a DELETE operation was fired. How would I have both logic inside of one trigger? Here is what I have so far:
ALTER TRIGGER [dbo].[Audit_Emp_Trigger]
ON [dbo].[EMPLOYEE]
AFTER UPDATE, DELETE
AS
BEGIN
--Only execute the trigger if the Dno field was updated or deleted
IF UPDATE(Dno)
BEGIN
--If the Audit_Emp_Record table does not exist already, we need to create it
IF OBJECT_ID('dbo.Audit_Emp_Record') IS NULL
BEGIN
--Table does not exist in database, so create table
CREATE TABLE Audit_Emp_Record
(
date_of_change smalldatetime,
old_Lname varchar (50),
new_Lname varchar (50),
old_ssn int,
new_ssn int,
old_dno int,
new_dno int
);
--Once table is created, insert the values of the update operation into the table
INSERT INTO Audit_Emp_Record(date_of_change, old_Lname, new_Lname, old_ssn, new_ssn, old_dno, new_dno) SELECT GETDATE(), D.Lname, I.Lname, D.Ssn, I.Ssn, D.Dno, I.Dno FROM inserted I JOIN deleted D ON I.Ssn = D.Ssn
END
ELSE
BEGIN
--The table already exists, so simply insert the new values of the update operation into the table
INSERT INTO Audit_Emp_Record(date_of_change, old_Lname, new_Lname, old_ssn, new_ssn, old_dno, new_dno) SELECT GETDATE(), D.Lname, I.Lname, D.Ssn, I.Ssn, D.Dno, I.Dno FROM inserted I JOIN deleted D ON I.Ssn = D.Ssn
END
END
END
You can test for the type of operation by seeing which of the magic-/pseudo-tables -- INSERTED and DELETED have data in them. I prefer to use something like the following:
DECLARE #Operation CHAR(1);
IF (EXISTS(SELECT * FROM inserted))
BEGIN
IF (EXISTS(SELECT * FROM deleted))
BEGIN
-- rows in both has to be an UPDATE
SET #Operation = 'U';
END;
ELSE
BEGIN
-- no rows in "deleted" has to be an INSERT
SET #Operation = 'I';
END;
END;
ELSE
BEGIN
-- no rows in "inserted" has to be a DELETE
SET #Operation = 'D';
END;
You can then use the #Operation variable in an IF statement to do one or the other of those operations.
Something like:
IF (#Operation = 'U')
BEGIN
--Only execute the trigger if the Dno field was updated or deleted
IF UPDATE(Dno)
BEGIN
{your current code here}
END;
END;
ELSE
BEGIN
{what to do if the operation is a DELETE goes here}
END;
Technically you don't need the ELSE condition that sets #Operation = 'I';, but if you are going to copy/paste this code into various triggers or keep around as a template then no harm in it handling all three conditions.
Also, just as a side-note, you don't need the ELSE condition of the IF OBJECT_ID('dbo.Audit_Emp_Record') IS NULL statement, nor the INSERT INTO Audit_Emp_Record that is just after the CREATE TABLE but before the END. Just do the CREATE TABLE if it doesn't exist and then do the INSERT outside of that test. Meaning:
IF UPDATE(Dno)
BEGIN
--If the Audit_Emp_Record table does not exist already, we need to create it
IF OBJECT_ID('dbo.Audit_Emp_Record') IS NULL
BEGIN
--Table does not exist in database, so create table
CREATE TABLE Audit_Emp_Record
...
END
INSERT INTO Audit_Emp_Record(...)
END

SQL Server : Triggers for Insert

create table tab(id int identity,task_id int,task_descp varchar(10),task_code varchar(10))
insert into tab values(7,'BUS','B')
insert into tab values(3,'CAR','C')
create table tab_detail( task_descp varchar(10),task_code varchar(10),color varchar(10))
create trigger tab_trigger on tab for insert as
declare #task_descp varchar(10)
declare #task_code varchar(10)
declare #task_id int
set #task_descp=i.task_descp from inserted i
set #task_code=i.task_code from inserted i
set #task_id=i.task_id from inserted i
if(#task_id=7)
insert into tab_detail values(#task_descp,#task_code,'BLUE')
if(#task_id=3)
insert into tab_detail values(#task_descp,#task_code,'GREEN')
go
I want to create a trigger for table tab where if I insert a record based on the task_id column a record has to be inserted into another table tab_detail.
When executing this I get this error:
Incorrect syntax near the keyword 'from'
Instead of:
set #task_descp=i.task_descp from inserted i
Try this:
select #task_descp=i.task_descp from inserted i
Or you could do this:
create trigger tab_trigger on tab for insert as
insert into tab_detail
select task_descp, task_code, case #task_id when 7 then 'BLUE' else 'GREEN' end
from inserted
where taskid in (7,3)
go
Change the SET to SELECT. Also, inserted is a recordset, not a single value. Fixing the code issue still might result in a run time issue!
This code should work fine for a recordset of information.
CREATE TRIGGER tab_trigger ON tab FOR INSERT AS
BEGIN
-- nothing to do?
IF (##rowcount = 0) RETURN;
-- do not count rows
SET NOCOUNT ON;
-- inserted data
INSERT INTO tab_detail
SELECT
i.task_descp,
i.task_code,
CASE i.taskcode
WHEN 7 THEN 'BLUE'
WHEN 3 THEN 'GREEN'
ELSE ''
END
FROM inserted i
WHERE i.task_code in (3, 7)
END
GO

Resources