SQL - How to create a trigger involving 2 separate tables? - sql-server

Create an INSERT trigger called checkPub on the 'publishers' table to add a row into the 'pub_info' table
1) when a row is inserted into the 'publishers' table with the values of the pub_id inserted into the publishers table,
2) NULL in the logo column, and
3) the text 'Newbie' in the pr_info column.
4) In addition, print a message stating the pub_id along with '9991 Inserted into 'pub_info' table'.
INSERT INTO publishers( pub_id, pub_name, city, country )
VALUES( '9905', 'New Publisher', 'Vancouver', 'Canada' );
CREATE TRIGGER checkPub
BEFORE INSERT OR UPDATE ON
(Publishers P JOIN pub_info PI
ON
P.pub_id = PI.pub_id)
FOR EACH ROW
Not sure how to write the conditions after this.

You do not need any kind of looping here. You simply need a basic insert statement. Here is an example of the entire trigger.
CREATE TRIGGER checkPub ON Publishers after INSERT AS
set nocount on;
insert pub_info
(
pub_id
, logo
, pr_info
)
select i.pub_id
, null
, 'Newbie'
from inserted i;
Since this has the look of homework I will let you figure out the last requirement.

Related

trying to do multiple inserts in one trigger using field dependent on first insert

I am trying to manipulate a bunch of table triggers that start with an insert into one event table (TB A). This insert fires a trigger (T1) that does an insert into a secondary table (TB B). The secondary table has an insert trigger (T2) that does an update on the first table (TB A).
Pardon the confusion but basically I wanted to ensure that for the first trigger, do a second insert in the same table using the values of the first insert.
BEGIN
SET NOCOUNT ON
declare #Time int
declare #DeleteLinger int
select #Time = convert(integer,value) from Systemproperty
where [name] = 'KeepStoreLingerTimeInMinutes'
select #DeleteLinger = convert(integer,value) from Systemproperty
where [name] = 'KeepDeleteLingerTimeInMinutes'
IF (#DeleteLinger >= #Time) SET #Time=#DeleteLinger+1
insert StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,GFlags)
select DateAdd(mi,#Time,getutcdate()), 1, I.ID, r.ID, r.GFlags
from inserted I, StorageRule r
where r.Active=1 and I.Active=0 and (I.OnlineCount > 0 OR
I.OnlineScreenCount > 0)
-- try and get the value that was just inserted into StorageQueue
select #SFlags=S.GFlags FROM StorageQueue S, StorageRule r, inserted I
WHERE r.ID = S.RuleID and I.ID = S.parameter
-- if a certain value do another insert into StorageQueue
If (#SFlags = 10)
INSERT INTO StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,StoreFlags)
VALUES(DateAdd(mi,#Time,getutcdate()), 1, (SELECT parameter
FROM StorageQueue),2, #SFlags)
END
The problem is that there seems to be either an issue that the record is not yet inserted because the variable #SFlags is null or some other trigger accesses the values and makes changes. My question is whether this is a good way to do it. Is it possible to retrieve a value into the variable from within a trigger because it seems whichever way I try it, it doesnt work.

EF4 RowCount issue on instead of insert trigger while updating an other table

I have some trouble with entityFramework 4. Here is the thing :
We have a SQL server database. Every table have 3 instead of triggers for insert, update and delete.
We know EntityFramework has some issues to deal with theses triggers, that's why we added the following code at the end of triggers to force the rowCount :
for insert :
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
for update/delete :
CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable
It worked fine until now :
From an instead of insert trigger (let's say table A), I try to update a field of an other table (table B)
I know my update code perfectly work since a manual insert does the work. The issue shows up only when I'm using Entity framework.
I have the solution now, let's make a school case of this with a full example. :)
In this example, our application is an addressBook. We want to update the business Activity (IsActive column in Business)
everytime we add, update or delete a contact on this business. The business is considered as active if at least one of the contact
of the business is active. We record every state changements on the business in a table to have the full history.
So, we have 3 tables :
table Business (Identifier (PK Identity), Name, IsActive),
table Contact (Identifier (PK Identity), Name, IsActive, IdentifierBusiness)
table BusinessHistory (Identifier (PK Identity), IsActive, Date, IdentifierBusiness)
Here's are the triggers one we are interested in :
table Contact (trigger IoInsert):
-- inserting the new rows
INSERT INTO Contact
(
Name
,IsActive
,IdentifierBusiness
)
SELECT
t0.Name
,t0.IsActive
,t0.IdentifierBusiness
FROM
inserted AS t0
-- Updating the business
UPDATE
Business
SET
IsActive = CASE WHEN
(
(t0.IsActive = 1 AND Business.IsActive = 1)
OR
(t0.IsActive = 1 AND Business.IsActive = 0)
) THEN 1 ELSE 0
FROM
inserted AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
-- Forcing rowCount for EntityFramework
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
Table Business (trigger IoUpdate)
UPDATE
Business
SET
IsActive = 1
FROM
Contact AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
---- Updating BusinessHistory
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.Identifier <> t1.Identifier)
-- Forcing rowCount for EntityFramework
CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable
Table BusinessHistory :
-- Updating the business
UPDATE
Business
SET
IsActive = CASE WHEN
(
(t0.IsActive = 1 AND Business.IsActive = 1)
OR
(t0.IsActive = 1 AND Business.IsActive = 0)
) THEN 1 ELSE 0
FROM
inserted AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
-- inserting the new rows
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
-- Forcing rowCount for EntityFramework
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
So, in a nutshell, what happened ?
We have 2 tables, Business and Contact. Contact is updating table Business on insert and update.
When Business is updated, it does an insert into BusinessHistory, which is storing the history of updates of table Business
,when the field IsActive is updated.
the thing is, even if I don't insert a new row in BusinessHistory, I launch an insert instruction and so, I go inside the instead of insert trigger of the table BusinessHistory. Of course, in the end of this one, there is a scope_identity(). You can use scope_identity only once, and it gives back the last identity inserted.
So, since I did not inserted any BusinessHistory, it was consuming the scope_identity of my newly inserted contact : the scope_identity of the instead of
insert of the contact table was empty !
How to isolate the issue ?
Using the profiler, you figure out that there are insert instruction in BusinessHistory when it should not be any of them.
Using the debugging, you will eventually end in the an insert trigger your are not supposed to be in.
How to fix it ?
Several alternatives here. What I did was to surround in table Business the insert of BusinessHistory by an If condition :
I want the insert to be inserted only if the statut "IsActive" has changed :
IF EXISTS
(
SELECT
1
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.IsActive <> t1.IsActive)
)
BEGIN
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.IsActive <> t1.IsActive)
END
An other possibility is, in the trigger instead of insert of the table BusinessHistory, to surround the whole trigger by an IF EXISTS condition
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
----Trigger's code here !
END
How to avoid it ?
Well, use one of these fixes !
Avoiding scope_identity(), ##IDENTITY is more than enough in most of the cases ! In my company, we only use scope_identity because of EF 4 !
I know my english is not perfect, I can edit if it's not good enough, or if someone want to add something on this subject !

TSQL trigger instead of insert

I need to replace, after an insert in a certain table, one of the column value if it match with a banned value.
example:
INSERT INTO [dbo].[TEST_A]
([COL_A]
,[COL_B])
VALUES
('TEST'
,'TEST1')
TEST1 is a banned value and I want to replace it in 'BANNED'.
This is the trigger I wrote but it seems to working properly well:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,REPLACE(COL_B, 'TEST1', 'BANNED')
FROM inserted
WHERE INSERTED.COL_B IN ('TEST1')
The error is that if I insert a different value in COL_B no rows are inserted.
Can you give me a solution?
thanks in advance
If you have more values than you want to put in a case statement, you can try using a table to store the banned words and the replacement for them. Then join to that in the insert, something like:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN b.banned is null then i.col_b
ELSE b.replacement
END
FROM inserted i
LEFT JOIN Banned b on i.col_B = b.banned
You need to modify your trigger.
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN COL_B ='TEST1' THEN 'BANNED'
WHEN COL_B ='TEST12' THEN 'BANNED34'
ELSE COL_B END
FROM inserted

Using Instead-Of SQL triggers to insert or update a view

I want to write a trigger to the view, VW_BANKBRANCH:
If the inserted row contains a bankcode that exists in the table, then update the
bName column of bank table with the inserted data
If not, insert rows to bank table to reflect the new information.
But my trigger is not working..
My tables
CREATE TABLE bank(
code VARCHAR(30) PRIMARY KEY,
bName VARCHAR(50)
);
CREATE TABLE branch(
brNum INT PRIMARY KEY,
brName VARCHAR(50),
braddress VARCHAR(50),
bcode VARCHAR(30) REFERENCES bank(code)
);
CREATE VIEW VW_BANKBRANCH
AS
SELECT code,bname,brnum,brName
FROM bank ,branch
WHERE code=bcode
My trigger
CREATE TRIGGER tr_VW_BANKBRANCH_INSERT ON VW_BANKBRANCH
INSTEAD OF INSERT
AS
BEGIN
DECLARE #insertedBankCode INT
#insertedbname varchar
#insertedbrnum int
#insertedbrName varchar
SELECT #insertedBankCode = code
FROM INSERTED
IF(#insertedBankCode=code)
SET code=#insertedBankCode
bname=#insertedbname
brnum=#insertedbrnum
brName=#insertedbrName
ELSE
insert(code,bname,brnum,brName)
END
I've adapted the instead of trigger on the view below - I'm assuming you want to upsert both bank and branch accordingly (although note that the branch address is not currently in the view).
That said, I would be careful of (ab)using an instead of trigger on an INSERT to do upserts - this might not be entirely intuitive to the reader.
Also, remember that the INSERTED pseudo table could contain a SET of rows, so needs to be adjusted to set based approach accordingly.
CREATE TRIGGER tr_VW_BANKBRANCH_INSERT ON VW_BANKBRANCH
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE b
SET bname = i.bname
FROM bank b
INNER JOIN inserted i
ON i.code = b.code;
UPDATE br
SET
br.brName = i.brName,
br.braddress = NULL -- TODO add this to the view
FROM branch br
INNER JOIN inserted i
ON br.bcode = i.code
AND br.brNum = i.brNum;
INSERT INTO bank(code, bname)
SELECT code, bname
FROM inserted i
WHERE NOT EXISTS
(SELECT 1 FROM bank b WHERE b.code = i.Code);
INSERT INTO Branch(brNum, brName, braddress, bcode)
SELECT brNum, brName, NULL, code
FROM inserted i
WHERE NOT EXISTS
(SELECT 1
FROM branch br
WHERE br.bcode = i.Code AND br.brNum = i.brNum);
END;
GO
SqlFiddle here - I've also adjusted the view to use a JOIN, rather than the old style of WHERE joins.
If you have SqlServer 2008 or later, you could also use MERGE instead of separate inserts and updates.

Unable to set value based on INSERTED table in trigger

GOAL:I'm creating after insert trigger that should insert new record to OrderSuspendRule table based on rule in this table that was related with Promotion of which new version was created.
PROBLEM
I cannot set value to #SUS_ID. Select returns value but it isn't set to variable.
Sample insert:
INSERT INTO PromotionHeader (Guid,CreatedAt,UpdatedAt,IsActive,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version],StatusId,PreviousId)
select newid(),CreatedAt,UpdatedAt,1,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version]+1,StatusId,916 FROM PromotionHeader WHERE Id=916
Where PreviousId points to older version of promotion.
CREATE TRIGGER TRIG1 ON DBO.PromotionHeader
AFTER INSERT
AS
DECLARE #SUS_ID INT
SET #SUS_ID = (
SELECT Max(id)
FROM OrderSuspendRule
WHERE PromotionHeaderId = (
SELECT PreviousId
FROM inserted
WHERE ID = SCOPE_IDENTITY()
)
AND ISACTIVE=1
)
IF (#SUS_ID IS NOT NULL) --**VARIABLE IS ALWAYS NULL NO MATTER WHAT**
BEGIN
INSERT INTO OrderSuspendRule (
Guid
,CreatedAt
,UpdatedAt
,IsActive
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,PromotionHeaderId
,SuspendTypeId
,OfferItemId
)
SELECT NEWID()
,GETDATE()
,GETDATE()
,1
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,SCOPE_IDENTITY()
,SuspendTypeId
,OfferItemId
FROM OrderSuspendRule
WHERE id = #SUS_ID
END
Inside a for insert trigger, you can assume that all rows in the inserted table were inserted. There is no need to double check this with scope_identity().
To explain why scope_identity() is null, remember that scope_identity() returns the last inserted identity in the current scope. Since your trigger runs in its own scope, this will always be null, unless the trigger itself performs an insert.
Also, be aware that your trigger can be run for an insert of multiple rows. That means you can't expect only a single #sus_id, there might be many.

Resources