SQL Server Trigger on INSERT always ROLLED BACK - sql

I want to create a trigger that will not allow to past a client out from USA.
So my code is:
CREATE TRIGGER location_tr
ON t1 FOR INSERT, UPDATE
AS
BEGIN
DECLARE #country VARCHAR(50)
SET #country = (SELECT country FROM inserted);
IF #country <> 'USA'
BEGIN
PRINT 'You cant add user out of USA!'
ROLLBACK TRANSACTION
END
ELSE
COMMIT TRANSACTION
END
So now, I can't do any UPDATE or INSERT on the table t1.
I guess I'm going wrong with a COMMIT/ROLLBACK commands. Need help. Thanks

The first issue is that your trigger does not support multiple rows. You should use EXISTS instead of setting the value of a variable. Something along these lines.
CREATE TRIGGER location_tr ON t1
FOR INSERT, UPDATE AS
BEGIN
IF EXISTS(select * from inserted where Country <> 'USA')
BEGIN
PRINT 'You cant add user out of USA!'
ROLLBACK TRANSACTION
END
END

Related

'INSERT' stored procedure with validation

I'm trying to create a stored procedure where I'm inserting a new office into the OFFICE table I have in my database.
I want to first check whether the office I'm trying to create already exists or not.
Here is some code from where I've gotten so far, but I'm not able to quite get it right. I would greatly appreciate some input.
CREATE PROCEDURE stored_proc_new_office
AS
BEGIN
DECLARE #office_id int
SELECT #office_id = (SELECT office_id FROM inserted)
IF NOT EXISTS (SELECT 1 FROM OFFICE WHERE office_id = #office_id)
BEGIN
ROLLBACK TRANSACTION
PRINT 'Office already exists.'
END
END
Here is a bare bones example of how you can use a stored procedure to insert a new record with a check to ensure it doesn't already exist.
create procedure dbo.AddNewOffice
(
#Name nvarchar(128)
-- ... add parameters for other office details
, #NewId int out
)
as
begin
set nocount on;
insert into dbo.Office([Name]) -- ... add additional columns
select #Name -- ... add additional parameters to match the columns above
where not exists (select 1 from dbo.Office where [Name] = #Name); -- ... add any additional conditions for testing for uniqueness
-- If nothing inserted return an error code for the calling app to use to display something meaningful to the user
if ##rowcount = 0 return 99;
-- return the new id to the calling app.
set #NewId = scope_identity();
return 0;
end

how to maintain table log (every changes) history in MS SQL by write trigger

We just want to maintain table log (every changes) history in MS SQL by write trigger please suggest
i tried but not working
CREATE TRIGGER [dbo].[update_ServiceDescriptionTable]
ON ServiceDescriptionMaster
After UPDATE
AS
BEGIN
declare #Rate money;
Select #Rate = Rate from inserted;
update [dbo].[ServiceDescriptionMasterlog] set Rate = #Rate
where Service_Description = '';
END
Ya good.
If you want to maintain evry changes log then you can insert in same log table with all field like as follows:
1) create same table like "ServiceDescriptionMasterlog" with one Extra Field (Column) Entry_DateTime set default bind getdate() method.
2) write a trigger on "ServiceDescriptionMaster" table as follows:
ALTER TRIGGER [dbo].[ServiceDescriptionMaster_OnUpdate]
ON [dbo].[ServiceDescriptionMaster]
After UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[ServiceDescriptionMasterLog]
(S_No,Rate,.....)
select S_No,Rate,.....
from Deleted;
END
you can also maintain on delete:
ALTER TRIGGER [dbo].[ServiceDescriptionMaster_OnDelete]
ON [dbo].[ServiceDescriptionMaster]
For Delete
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[ServiceDescriptionMasterLog]
(S_No,Rate,.....)
select S_No,Rate,.....
from Deleted;
END

Insert Update Duplicate Value SQL

I have this stored procedure:
alter procedure spGroupInsert
(#Group varchar(5))
as
if not exists (select * from tbGroup where Group = #Group)
begin
insert into tbGroup(Group)
values(#Group)
end
else
begin
waitfor delay '00:00:01'
end
The stored procedure is designed to prevent duplicates on tbGroup. Next, I need to have an UPDATE stored procedure:
alter procedure spGroupUpdate
(#GroupID int, #Group varchar(5))
as
begin
update tbGroup
set Group = #Group
where GroupID = #GroupID
end
The table should be:
GroupID Group
1 A
2 B
3 C
4 D
ff.
GroupID is identity. For Insert SP, I am really sure there won't be a problem.
But, if I execute the Update stored procedure, then I change the Group. It will be a duplicate. For instance, if I update Group A to B. Then it will make a duplicate of B.
How I can prevent this in T-SQL in my Update stored procedure?
Thank you.
ALTER TABLE tbGroup
ADD CONSTRAINT UC_Group UNIQUE (Group);
this will handle that in the update stored procedure. It will not update when there is an existing row with the same Group
alter procedure spGroupUpdate
(#GroupID int, #Group varchar(5))
as
begin
update tbGroup
set Group = #Group
WHERE GroupID = #GroupID
AND NOT EXISTS
(
SELECT *
FROM tbGroup x
WHERE x.GroupID <> #GroupID
AND x.Group = #Group
)
end
You are saying that for spGroupInsert you are sure that there won't be a problem. In fact, there is a problem.
This procedure doesn't guarantee that you'll never insert a duplicate. If two sessions are trying to insert the same value at the same time, you can easily get duplicates.
Both sessions can do the check if not exists at the same time and both can proceed with INSERT.
alter procedure spGroupInsert
(#Group varchar(5))
as
if not exists (select * from tbGroup where Group = #Group)
begin
insert into tbGroup(Group)values(#Group)
end
else
begin
waitfor delay '00:00:01'
end
The only way to guarantee that Group values are unique is to create a unique constraint, which is usually implemented as a unique index.
CREATE UNIQUE NONCLUSTERED INDEX [IX_Group] ON [dbo].[tbGroup]
(
[Group] ASC
)
GO
With such unique index in place one of the sessions from the example above would fail to INSERT the duplicate and the caller of the stored procedure would receive an error message about unique constraint violation. The caller would need to decide what to do about this error, how to handle it.
The check if not exists reduces the chances of getting this error, but it can't prevent it completely. So, with the check if not exists or without the check if not exists the INSERT can fail and your code should be able to handle this situation.
It seems that you are happy to suppress/ignore the error. In this case a simple TRY ... CATCH can be enough.
alter procedure spGroupInsert
(#Group varchar(5))
as
BEGIN
SET NOCOUNT ON; SET XACT_ABORT ON;
BEGIN TRANSACTION;
BEGIN TRY
if not exists (select * from tbGroup where Group = #Group)
begin
insert into tbGroup(Group)values(#Group);
end
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END
With this stored procedure (and unique index), if two sessions try to call it with the same Group value at the same time, only one will actually insert the value and the second would silently fail and do nothing. The caller would not know about this collision. In your case it may be acceptable.

Using Trigger to keep data integrity

I have scoured the internet for a solution (mostly scouring stack overflow) and I cannot come up with anything.
Here is my goal: I have a local database and I have set up a linked server to another database. I am creating a trigger on one of my local tables. One of the column values is a Hotel ID. In the linked server there is a table called "Hotel". The point of this trigger is to check and make sure that the HotelID I am trying to insert into my local table is a value that exists in the linked server's Hotel table.
Example: If I want to insert a new row into my "Store Table" from local, I want to make sure that the HotelID I am trying to insert exists in the "Hotel" table in my linked server. If it does not exist, I want to rollback the transaction and display a message.
Below is the code I have been playing with. I feel like I could be close, but I am open to the idea that I am extremely far away.
FYI: The code inside of the IF NOT EXISTS statement is incorrect. I am just confused as to what needs to go in there.
CREATE TRIGGER tr_trigger ON Store
AFTER Insert
AS
DECLARE #HotelID smallint = (SELECT HotelID FROM inserted)
DECLARE #query NVARCHAR(MAX) = N'SELECT * FROM OPENQUERY (test,''
SELECT HotelID FROM test.dbo.Hotel WHERE HotelID = ''''' +
CONVERT(nvarchar(15),#HotelID) +''''''')'
DECLARE #StoredResult Nvarchar(20)
BEGIN
EXEC sp_executesql #query, N'#StoredResult NVARCHAR(MAX) OUTPUT', #StoredResult =
#StoredResult OUTPUT
SELECT #StoredResult
IF NOT EXISTS (SELECT * FROM OPENQUERY (test,' SELECT HotelID FROM test.dbo.Hotel'))
BEGIN
PRINT'That HotelID does not exist. Please try again.'
ROLLBACK TRANSACTION
END
END
GO
EDIT: This has been solved thanks to a couple of suggestions from marc_s. Below is my new code that works how I need it to.
CREATE TRIGGER tr_trigger ON Store
AFTER Update, Insert
AS
BEGIN
IF NOT EXISTS (SELECT A.* FROM OPENQUERY (test, 'SELECT HotelID FROM test.dbo.hotel') A
INNER JOIN inserted i
ON A.HotelID = i.HotelID)
BEGIN
PRINT'Please enter a valid HotelID'
ROLLBACK TRANSACTION
END
END
GO
How about:
CREATE TRIGGER tr_DataIntegrity ON Store
AFTER Update, Insert
AS
BEGIN
IF EXISTS (
SELECT * FROM inserted i
WHERE NOT EXISTS (
SELECT A.*
FROM OPENQUERY (TITAN_Prescott_Store, 'SELECT HotelID FROM FARMS_Prescott.dbo.hotel') A
WHERE A.HotelID = i.HotelID))
BEGIN
PRINT'Please do not enter an invalid HotelID'
ROLLBACK TRANSACTION
END
END
GO

SQL Trigger on Insert, Delete, Updated

I have the following trigger
ALTER TRIGGER [dbo].[RefreshProject]
ON [dbo].[Project]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #percentage decimal(18,2)
DECLARE #ProjectID int
DECLARE #TASKID int
IF EXISTS(SELECT * FROM Inserted)
BEGIN
SELECT #ProjectID = Inserted.ProjectID, #TASKID = ISNULL(Inserted.TASKID,-1) from Inserted join Project on Inserted.ScheduleID = Project.ScheduleID
END
IF EXISTS(SELECT * FROM Deleted)
BEGIN
SELECT #ProjectID = Deleted.ProjectID,#TASKID = ISNULL(Deleted.TASKID,-1) from Deleted join Project on Deleted.ScheduleID = Project.ScheduleID
END
BEGIN
SET #percentage = (SELECT percentage from Project where ProjectID = #ProjectID)
EXEC LoadSummary #percentage,#ProjectID, #TASKID
END
END
For Inserts and updates I am able to get the ProjectID of the modified object, however when an item is deleted, I can not get the ProjectID or TaskID... Any Idea what I'm doing wrong?
The problem is that your trigger is an AFTER trigger, which means that the row has already been deleted from the Project table by the time the trigger is fired. So, you can't join the deleted view to Project, because the corresponding row won't exist. You will need to assign the variables you want directly from the deleted view.
Also, as Mitch's note mentions above, you may want to use a cursor to iterate over all rows in the inserted/deleted views if multiple rows can be updated at a time. Alternatively, you could raise an error at the beginning of your trigger if ##ROWCOUNT is greater than one, to prevent multiple row updates from causing your trigger to misbehave.