Referencing Columns on the Inserted Table T-SQL - sql-server

So, I'm trying to create a trigger that will throw an error on inserting data if a foreign key code is not valid. I have two tables, Publisher and Title. Title has a publisher code on it, as does Publisher. I have the trigger on my Title for insert, and I am currently doing an if not exists and selecting the Publisher row where the code is equal to the inserted row's publisher code. I don't know if this is the right way to do it, and probably not, as SQL is giving me a "multi-part identifier Inserted.PublisherCode could not be found" error. Any help you guys could give would be appreciated. Thanks.
go
create trigger TR_Title_PublisherCode_Insert
on title
for Insert
as
if not exists(select * from Publisher where PublisherCode = Inserted.PublisherCode)
begin
raiserror('Publisher does not exist', 16, 1)
rollback tran
end

INSERTED and DELETED are table too
So you have to do this:
if not exists(
select * from Publisher
where PublisherCode in (SELECT PublisherCode FROM inserted)
)
By the way, as hkf said, if you identified PublisherCode AS foreign key, you won't have a need to make trigger

Related

Re-Inserting deleted rows into the same table SQL Server 2005

After searching many pages I still can't find the answer about re-inserting deleted rows in the same table - not another table.
I have a table named timetable with the primary key made up from 3 columns Schoolcode, Year, Term.
I need for some reason need to insert deleted rows into the same table.
I get the error
Violation of PRIMARY KEY constraint
with the following trigger
ALTER TRIGGER [dbo].[AFTER_delete_]
ON [dbo].timetable
AFTER delete
AS
BEGIN
IF EXISTS (SELECT * FROM deleted)
BEGIN
INSERT INTO timetable
SELECT *
FROM deleted A
WHERE NOT EXISTS (SELECT 1 FROM timetable B
WHERE B.Schoolcode = A.Schoolcode
AND B.Year = A.Year
AND B.Term = A.Term);
END
END
thanks any way.I test the code below and that did work.
ALTER TRIGGER [dbo].[Instead_OfDelSert_Status]
ON [dbo].[Status]
INSTEAD OF delete,insert
AS
BEGIN
PRINT 'You must disable or delete Trigger Instead_OfDelSert_Status to insert or
delete rows!'
END

Correct way to drop a table in SQL Server

What is the best way to drop a table? The code I have does it two different ways. It will be dropped every week when I do a bulk insert. Thanks.
Like this?
IF EXISTS (SELECT *
FROM DBO.SYSOBJECTS
WHERE ID = OBJECT_ID(N'[DBO].[ZIP]') AND OBJECTPROPERTY(ID, N'IsUserTable') = 1)
DROP TABLE [DBO].[ZIP]
Or this?
DROP TABLE DEID.DBO.ZIP
The second method will throw an error if the table doesn't exist, or if other database objects depend on it.
The first one will not throw an error if the table doesn't exist, but it will still throw an error if other database objects depend on it.
Check out this answers on Stack-overflow:
DROP IF EXISTS VS DROP?
How to drop a table if it exists?

Exception after dropping table

If I execute a procedure that drops a table and then recreate it using 'SELECT INTO'.
IF that procedure raises an exception after dropping the table, does table dropping take place or not?
Unless you wrap them in a transaction,table will be dropped since each statement will be considered as an implicit transaction..
below are some tests
create table t1
(
id int not null primary key
)
drop table t11
insert into t1
select 1 union all select 1
table t11 will be dropped,even though insert will raise an exception..
one more example..
drop table orderstest
print 'dropped table'
waitfor delay '00:00:05'
select * into orderstest
from Orders
now after 2 seconds,kill session and you can still see orderstest being dropped
I checked with some other statements other than select into ,i don't see a reason why select into will behave differently and this applies even if you wrap statements in a stored proc..
IF you want to rollback all,use a transaction or more better use set xact_Abort on
Yes, the dropped table will be gone. I have had this issue when I script a new primary key. Depending on the table, it saves all the data to a table variable in memory, drops the table, creates a new one with the new pk, then loads the data. If the data violates the new pk, the statement fails and the table variable is dropped leaving me with a new table and no data.
My practice is to create the new table with a slightly different name, load the data, change both table names in a statement, then once all the data is confirmed loaded, drop the original table.

SQL Server 2012 foreign key constraint trigger

I am new to triggers and I have created a trigger to check foreign key constraints across tables in different databases. I know this shouldn't be done however this is the only solution that I could find to resolve my issue with foreign key constraints. The trigger does work however it does not tell me what record caused the violation when the insert script has more than one record to insert. I am looking for a way that the trigger could tell me the record with 'x' primary key and 'y' foreign key was the one to fail. Currently, it runs thru the script comes across a violation, throws the RAISERROR, rolls back everything and nothing gets inserted in the database. Below is my script -
Create Trigger AV.fkConstraintTrigger ON [AQB_MON].[AV].[NAAQValue]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT *
FROM INSERTED AS I
WHERE NOT EXISTS (
SELECT *
FROM [AVData].[dbo].[SourceParameterTemplate] AS A
WHERE I.[SourceParameterTemplateID] = A.[SourceParameterTemplateID]
)
)
BEGIN
RAISERROR('Violation of foreign key constraint',16,1);
ROLLBACK;
END
END
UPDATE
I made changes to the script based on the comments below because I would rather have them displayed in the message then in a separate table. However when I do I get two 'Incorrect syntax near' errors. First being the '=' and the second is the last ')'. I cannot see what would cause them.
Create Trigger AV.testfkTrigger ON [AQB_MON].[AV].[NAAQValue]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
Declare #SourceParameterTemplateID varchar(25)
IF EXISTS (
SELECT TOP 1 #SourceParameterTemplateID = [SourceParameterTemplateID]
FROM INSERTED AS I
WHERE NOT EXISTS (
SELECT *
FROM [AVData].[dbo].[SourceParameterTemplate] AS A
WHERE I.[SourceParameterTemplateID] = A.[SourceParameterTemplateID]
)
order by [SourceParameterTemplateID]
)
BEGIN
RAISERROR('Violation of foreign key constraint',16,1, #SourceParameterTemplateID);
ROLLBACK;
END
END
Putting the data into the error message is not very useful. What would make more sense is to create and exception table that stores the data and possibly any other useful fields like the time of the problem and the user who sent in the bad data.
If you temporarily put the info into a table variable, then you can insert to the exception table after the rollback as the table variable is not rolled back.
Then your application can look up the data in the table if an error is returned from the insert/update.

SQL Server before trigger

I need to control table values uniqueness. It cannot be done by an index or a constraint (error message must show data from another table). I thought of after trigger but since it fires after the insert the below trigger will fire even if values are unique.
--table
CREATE TABLE Names (Id IDENTITY(1,1) NOT NULL, Name VARCHAR(20) NOT NULL)
--first record
INSERT INTO Names VALUES ('John')
--trigger
CREATE TRIGGER [dbo].[Names_Insert_Trigger]
ON [dbo].[Names]
FOR INSERT, UPDATE
AS
SET NOCOUNT ON
IF EXISTS (SELECT Name
FROM inserted
WHERE EXISTS (SELECT * FROM Names N JOIN inserted ON N.Name=inserted.Name))
BEGIN
RAISERROR('This name is already registered in file XYZ.', 16, 1)
ROLLBACK TRAN
SET NOCOUNT OFF
RETURN
END
SET NOCOUNT OFF
--I add another record with different value and the trigger fires
INSERT INTO Names VALUES ('Steven')
I also thought of an instead of insert trigger but the actual table has identity set and will likely get new columns in the future which would require updating the trigger code at each change so I can't use the below code:
CREATE TRIGGER [dbo].[Names_Insert_Trigger]
ON [dbo].[Names]
INSTEAD OF INSERT
AS
SET NOCOUNT ON
IF EXISTS (SELECT Name
FROM inserted
WHERE EXISTS (SELECT * FROM Names N JOIN inserted ON N.Name=inserted.Name))
BEGIN
RAISERROR('This name is already registed in file XYZ.', 16, 1)
ROLLBACK TRAN
SET NOCOUNT OFF
RETURN
END
ELSE
INSERT INTO Names
SELECT * FROM inserted
SET NOCOUNT OFF
Any ideas how to solve it?
Regards,
Przemek
You can use an after trigger. Just use COUNT instead of EXISTS. You should still have a non-unique index on name to optimize performance and concurrency.
IF (SELECT COUNT(*)
FROM inserted AS i
JOIN dbo.Names AS N ON
N.Name = i.Name
GROUP BY N.Name
) > 1
BEGIN
RAISERROR...
END;
The real solution is to use the UNIQUE constraint to this problem, it's designed to solve it and it's much more performant and safer than a trigger for this usage. The error message is better built client-side and ignore the server genereated one, save for determining the exact reason.
But if you really want to follow the trigger route, use the AFTER version, but fix the query for detect duplicates:
SELECT * FROM (
SELECT Name
FROM Names
WHERE id NOT IN (SELECT id FROM inserted)
) PreviousNames
INNER JOIN inserted ON PreviousNames.Name = inserted.Name
(I'm just showing the query to check duplication that goes into the IF EXIST instruction, not the whole trigger).
It begins by creating a subquery that gets the names NOT being inserted (so that you don't get a false positive), then simply joins again to inserted to check if any value is in both tables.
There is an additional problem that can happen when using SNAPSHOT issolation level. In this mode, the trigger will NOT see the changes made by other connections, nor they'll be blocked until the trigger ends. I'm not quite familiar with the details, but will leave this article as reference and possible solutions: https://sqlserverfast.com/?s=snapshot+integrity

Resources