Is there any way to store field value only once? - sql-server

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

Related

AFTER DELETE, UPDATE trigger not working correctly in SQL

Something peculiar is happening with one of my triggers. I have built a trigger which will check if the user is attempting to UPDATE or DELETE a row in the table which has [Released_Flag] = 'Y', and rollback the transaction if that is the case. However, if the user is attempting to update the Released_Flag field from 'N' to 'Y' for a row, it still returns the error.
My trigger is:
CREATE TRIGGER [dbo].[Prevent_Delete] ON [dbo].[Data_Test]
AFTER DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #Release_Flag char(1)
SELECT #Release_Flag = [Release_Flag] FROM deleted
IF #Release_Flag='Y'
BEGIN
RAISERROR('Cannot update or delete a RELEASED variable.',16,1)
ROLLBACK
END
END
The above is causing some issues in UPDATE statements. It is sometimes raising the error, even if the row being updated has Release_Flag = 'N'. Any idea why this might be happening?
I also used SELECT * FROM [deleted] within the trigger to see what is happening, and some rows are showing Release_Flag = 'Y' in the [deleted] table, despite them actually being 'N'.
The issue with this that I did not realise is I have another trigger on the table which UPDATES the table. And so the old trigger is firing this new trigger, something I hadn't accounted for.
I have now moved the AFTER UPDATE section of this new trigger into my old trigger.

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

SQL - Setting a Default Value from Trigger

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.

SQL Server 2008 Update Trigger

I need to create an update trigger on a table that it also updates the table if certain inserted values are present. Is this possible? I have tried and when the inserted value is present and fires the trigger the trigger does an update and fires itself again. Eventually - deadlock. Is there a way to accomplish this safely?
The Code:
CREATE TRIGGER dbo.trg_updateaddress on dbo.Customers
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #original_source varchar(50)
SELECT #original_source = address_source FROM deleted
IF EXISTS (SELECT * FROM inserted i
INNER JOIN deleted d
ON i.cust_id = d.cust_id
WHERE i.address_source IS NOT NULL
AND (i.[address] <> d.[address]
OR i.address2 <> d.address2
OR i.city <> d.city
OR i.[state] <> d.[state]
OR i.zip <> d.zip
OR i.country <> d.country
OR i.phone <> d.phone
OR i.cell <> d.cell
OR i.email <> d.email))
BEGIN
UPDATE customers
SET address_changed = GETDATE()
END
ELSE
BEGIN
UPDATE customers
SET address_source = #original_source
END
END
One thing that you can do is to disable trigger recursion in your database:
USE [master]
GO
ALTER DATABASE [yourDbName] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO
This will prevent all triggers in the database from triggering themselves directly, while still allowing them to fire other triggers.
I normally recommend this setting anyway, as recursive triggers is not normally what you want, and a very specialized behavior.
You can prevent the trigger from firing on itself with NESTED_LEVEL
Put this after SET NOCOUNT ON;
IF trigger_nestlevel() < 2
This will prevent the trigger from firing on it's own update.
Note: trigger_nestlevel starts counting at 1 for the first update, so if you want the trigger to only fire once then set it to 2, if you want it to fire twice then set this to 3.

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.

Resources