SQL - Setting a Default Value from Trigger - sql-server

I'm currently working on writing a trigger for my database, with table employee and column dno. Before writing the trigger, I specified dno to have a default value of 1, and in the trigger I want to enforce the default value if the value in dno is to be deleted.
This is what I have so far. It seems pretty straightforward to me, but I feel like I'm missing a lot.
create trigger empdeptfk_delete
on employee
after delete as
begin
update employee
set dno = 1
end
Is there a way I can write set default value instead of set dno = 1? Also, I'm using SQL Server 2012, if that helps. Thanks!

You have to handle the Update trigger instead of the delete trigger, because you need to catch when dno field is being UPDATED.
Here is the code:
CREATE TRIGGER empdeptfk_update ON employee
INSTEAD OF UPDATE
AS
BEGIN
IF UPDATE(dno)
BEGIN
DECLARE #DNO INT
SELECT #DNO = dno
FROM Inserted
IF #DNO IS NULL
SET #DNO = 1
UPDATE employee
SET employee.dno = #DNO
FROM Inserted
WHERE Inserted.employeeID = employee.employeeID
END
END

First create 'trigger before delete' then do the update dno=1 and then do the delete.

Related

How to run SQL trigger

I am completely new to SQL Server 2008 and I wrote a trigger and would like to be executed only of hassubproduct and spdisplaytype columns are updated or inserted and if they have a value and are not empty.
Any help is appreciated.
CREATE TRIGGER [dbo].[hassubproductcheck]
ON [dbo].[products]
WITH EXECUTE AS CALLER
FOR INSERT, UPDATE
AS
BEGIN
UPDATE products
SET hassubproduct = LTRIM(RTRIM(hassubproduct))
UPDATE products
SET spdisplaytype = LTRIM(RTRIM(spdisplaytype))
END
GO
Something along these lines is probably more like what you want.
CREATE TRIGGER [dbo].[hassubproductcheck] ON [dbo].[products]
WITH EXECUTE AS CALLER FOR INSERT, UPDATE
AS BEGIN
Update p
set hassubproduct = LTRIM(RTRIM(i.hassubproduct))
, spdisplaytype = LTRIM(RTRIM(i.spdisplaytype))
from Products p
join inserted i on i.PrimaryKey = p.PrimaryKey
where i.hassubproduct > ''
OR i.spdisplaytype > ''
END
You have to use the below code to define the trigger. This will help for updated. The same way we need to create one for inserted as well.
CREATE TRIGGER [dbo].[hassubproductcheck] ON [dbo].products]
AFTER UPDATE
AS
INSERT INTO DBO.SAMPLE_TRIGGER
SELECT hassubproduct, 'UPDATE(PREVIOUS)' [TABLE-UPDATE] FROM DELETED
GO
CREATE TRIGGER [dbo].[hassubproductcheck] ON [dbo].products]
AFTER UPDATE
AS
INSERT INTO DBO.SAMPLE_TRIGGER
SELECT hassubproduct, 'UPDATE (LATEST)' [TABLE-UPDATE] FROM DELETED
GO

MS SQL trigger instead of update ELSE possible to execute original query?

Here is my trigger
Create TRIGGER [dbo].[tri_before_update]
ON [dbo].[test]
instead of update
AS
BEGIN
SET NOCOUNT ON;
if update (test_a)
begin
*.. my update & insert query*
end
END
create TRIGGER [dbo].[tri_before_update_price]
ON [dbo].[co_ticket]
instead of update
AS
BEGIN
SET NOCOUNT ON;
if update (t_price)
begin
insert into old_price_log (t_id,insert_time,process_id,old_t_price)
select i.t_id,getdate(),2,t_price
from Inserted i,co_ticket t where i.t_id = t.t_id
update t set t_price = i.t_price
from co_ticket t, inserted i
where t.t_id = i.t_id
end
else
begin
-- if update other then (t_price) then the update comand not execute.
-- example when i update t_cancel_flag or t_quantity and etc. end
END
This trigger execute perfectly when i update on column "test_a". HOWEVER, when i update other than column "test_a" it won't be execute. I know i can put "else" command, but i got a lot of column. sometimes will update two other column , sometimes three or four column. I don't wish to update all column everytime. Is it possible ELSE "then execute original query"?
I tried a lot different way but still can't work. :( Please HELP!
create TRIGGER [dbo].[tri_on_update_price]
ON [dbo].[co_ticket]
AS
BEGIN
SET NOCOUNT ON;
if update (t_price)
begin
insert into old_price_log (t_id,insert_time,process_id,old_t_price)
select d.t_id, getutcdate(),2,d.price
from deleted d
END
end
An ordinary after trigger will do just what you want: insert a log of the price change, if the price was updated. No need for INSTEAD OF. You need to look into the deleted pseudo-table to get the old price. Never store local times in a database.

Is there any way to store field value only once?

Is there any way to store (while inserting) a value on column which can never be changed ?
Like example :
insert to DB row "a,b,c,x,s,X" - and the X *wont* be able to change.
(however , if the whole row is deleted - its fine"
You can remove UPDATE privilieges for that column from the users you don't want to be able to change the value. Alternatively, use an update trigger to prevent updates.
The update trigger is easier if you want to disallow updates from any user.
You can write a trigger before updating or after updating ( http://msdn.microsoft.com/en-us/library/ms188601.aspx ) the rows to check whether it is going to change or not. Here is an example trigger after update , it will rollback transaction if column modified.
CREATE TRIGGER tr_update on YourTable AFTER UPDATE AS
IF UPDATE(YourColumn)
BEGIN
RAISERROR ('cannot change yourColumn', 16, 1)
ROLLBACK TRAN
RETURN
END
GO
Add an Update Trigger, where you check for a change:
CREATE TRIGGER YourTrigger On YourTable FOR UPDATE
AS
IF EXISTS (SELECT 1
FROM INSERTED i
INNER JOIN DELETED d ON i.PK=d.PK
WHERE i.columnX!=d.columnX
OR (i.columnX IS NULL and d.columnx IS NOT NULL)
OR (d.columnX IS NULL and i.columnx IS NOT NULL)
)
BEGIN
ROLLBACK
RAISERROR('Error, can''t change columnX',16,1)
RETURN
END
GO

preventing a specific record from being deleted

I want to prevent a specific record from being deleted. This trigger works fine for that specific record. However, other records still remain when they're being deleted. Why?
ALTER TRIGGER [Globalization].[CountriesTracker]
ON [Globalization].[Countries]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
IF ((Select COUNT(*) from [Deleted]
Where [Deleted].[CountryId] = '36bd1536-fb56-4ec4-957e-1b3afde16c56') = 1)
BEGIN
RAISERROR('You can not delete this specific record!', 0, 0)
ROLLBACK TRANSACTION
RETURN
END
END
How can I ensure that rows not matching the above condition are being deleted as expected?
You have an INSTEAD OF trigger so you need an actual DELETE in it.
I'd also consider simply filtering the protected row out because:
Do you need an error throwing? Or silently ignore?
What about multi row deletes that contain the protected row: abort the whole, or delete the rest?
Something like:
ALTER TRIGGER [Globalization].[CountriesTracker] ON [Globalization].[Countries]
INSTEAD OF DELETE
AS
SET NOCOUNT ON;
DELETE
CT
FROM
[Globalization].[Countries] C
JOIN
DELETED D ON C.CountryId = D.CountryId
WHERE
[Deleted].[CountryId] <> '36bd1536-fb56-4ec4-957e-1b3afde16c56'
GO
Because this is INSTEAD OF you still need to perform the delete operation for the default case.

Pass a variable into a trigger

I have a trigger which deals with some data for logging purposes like so:
CREATE TRIGGER trgDataUpdated
ON tblData FOR UPDATE
AS
BEGIN
INSERT INTO tblLog ( ParentID, OldValue, NewValue, UserID )
SELECT deleted.ParentID, deleted.Value, inserted.Value,
#intUserID -- how can I pass this in?
FROM inserted INNER JOIN deleted ON inserted.ID = deleted.ID
END
How can I pass in the variable #intUserID into the above trigger, as in the following code:
DECLARE #intUserID int
SET #intUserID = 10
UPDATE tblData
SET Value = #x
PS: I know I can't literally pass in #intUserID to the trigger, it was just used for illustration purposes.
I use SET CONTEXT_INFO for this kind of action. That's a 2008+ link, prior link has been retired.
On SQL Server 2005+, you'd have CONTEXT_INFO to read it but otherwise you have to get from context_info column in dbo.sysprocesses.
you can't pass a variable into a trigger.
the only way to get the information in the trigger is to be able to SELECT it based on the INSERTED or DELETED tables or add a column onto the affected table and put the value in that column.
EDIT in the previous question OP posted about this, they said that they didn't want to use CONTEXT_INFO, but here they say it is Ok to use, so here is a CONTEXT_INFO usage example:
in the procedure doing the update
DECLARE #intUserID int
,#CONTEXT_INFO varbinary(128)
SET #intUserID = 10
SET #CONTEXT_INFO =cast('intUserID='+CONVERT(varchar(10),#intUserID)+REPLICATE(' ',128) as varbinary(128))
SET CONTEXT_INFO #CONTEXT_INFO
--do update that will fire the trigger
SET CONTEXT_INFO 0x0
here is the portion of the trigger to retrieve the value:
DECLARE #intUserID int
,#sCONTEXT_INFO varchar(128)
SELECT #sCONTEXT_INFO=CAST(CONTEXT_INFO() AS VARCHAR) FROM master.dbo.SYSPROCESSES WHERE SPID=##SPID
IF LEFT(#sCONTEXT_INFO,9)='intUserID'
BEGIN
SET #intUserID=RIGHT(RTRIM(#sCONTEXT_INFO),LEN(RTRIM(#sCONTEXT_INFO))-10)
END
ELSE
BEGIN
RAISERROR('intUserID was not specified',16,1)
ROLLBACK TRAN
RETURN
END
..use the #intUserID
Old question, but I wonder how come nobody mentioned that temporary tables created before the trigger is invoked are visible in the trigger?
So, this would work:
SELECT 10 intUserID INTO #intUserID
UPDATE tblData
SET Value = #x
The trigger will see the temp table #intUserID and can read the id from there.
I use the sp_set_session_context stored procedure to set the value:
exec sp_set_session_context #key = N'userid', #value = 123
And in my trigger to read the value:
DECLARE #userid int
SELECT #userid = cast(SESSION_CONTEXT(N'userid') as int)
You cant pass variables to triggers. Depending on how users connect to the database you could use SYSTEM_USER to get the current user connected to the database.
You do not pass variables to triggers because you are not able to call triggers directly. They are executed as a result of data being inserted, modified or deleted.

Resources