I am writing a trigger. Whenever I insert multiple values into my table, NFL.Widereceivers, I want it to automatically insert these values into another table, AFC.North. I have written a trigger, and it works to an extent:
begin
declare
#name varchar(30),
#team varchar(3),
#receptions int,
#yards int,
#touchdowns int
select #name = Name from inserted
select #team = Team from inserted
select #receptions = Receptions from inserted
select #yards = Yards from inserted
select #touchdowns = Touchdowns from inserted
if (#team = 'PIT' or #team = 'BAL' or #team = 'CIN' or #team = 'CLE')
begin
insert into AFC.North (Name, Team, Receptions, Yards, Touchdowns)
values (#name, #team, #receptions, #yards, #touchdowns);
end
end
However, this trigger does not work if I insert multiple values into NFL.Widereceivers, only the first row is inserted into AFC.North.
How can I make the trigger insert multiple rows of data?
Your trigger makes a common but unfortunate mistaken assumption that all statements that fire them will affect exactly one row. Unlike some other platforms, a trigger fires per statement, not per row. So, you need to treat inserted like a set, and therefore stop assigning individual values to variables.
INSERT AFC.North(Name,Team,Receptions,Yards,Touchdowns)
SELECT Name,Team,Receptions,Yards,Touchdowns
FROM inserted WHERE Team IN ('BAL','CIN','CLE','PIT');
You also need to decide what to do for the rows that are in inserted for the other divisions (hint: you will need an INSERT statement per division, I suspect). Of course a better design would have the division as a column, rather than have each division with its own table.
Why are you assigning values in variables in trigger, you can insert it directly in table like below. If you assign values in variables then it will store values for one row at a time. It will work fine if you insert records one by one, but not work in multiple.
insert into AFC.North (Name, Team, Receptions, Yards, Touchdowns)
select Name, Team, Receptions, Yards, Touchdowns
from inserted
where Team IN ('PIT','BAL','CIN','CLE')
Even your variable code also can be optimized in single query like
select #name = Name, #team = Team, #receptions = Receptions, #yards = Yards, #touchdowns = Touchdowns from inserted
Related
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.
I am creating a a trigger in SQL that will insert into another table after Insert on it. However I need to fetch a Value from the table to increment to be used in the insert.
I have a AirVisionSiteLog table. On insert on the table I would like for it to insert into another SiteLog table. However in order to do this I need to fetch the last Entry Number of the Site from the SiteLog table. Then on its insert take that result and increase by one for the new Entry Number. I am new to Triggers and Functions so I am not sure how to use them correctly. I believe I have a function to retrieve and increment the Entry Number however I am not sure how to use it in the Trigger.
My Function -
CREATE FUNCTION AQB_RMS.F_GetLogEntryNumber
(#LocationID int)
RETURNS INTEGER
AS
BEGIN
DECLARE
#MaxEntry Integer,
#EntryNumber Integer
Set #MaxEntry = (Select Max(SL.EntryNumber) FROM AQB_MON.AQB_RMS.SiteLog SL
WHERE SL.LocationID = #LocationID)
SET #EntryNumber = #MaxEntry + 1
RETURN #EntryNumber
END
My Trigger and attempt to use the Function -
CREATE TRIGGER [AQB_RMS].[SiteLogCreate] on [AQB_MON].[AQB_RMS].[AirVisionSiteLog]
AFTER INSERT
AS
BEGIN
declare #entrynumber int
declare #corrected int
set #corrected = 0
INSERT INTO [AQB_MON].[AQB_RMS].[SiteLog]
([SiteLogTypeID],[LocationID],[EntryNumber],[SiteLogEntry]
,[EntryDate],[Corrected],[DATE_CREATED],[CREATED_BY])
SELECT st.SiteLogTypeID, l.LocationID,
(select AQB_RMS.F_GetLogEntryNumber from [AQB_MON].[AQB_RMS].[SiteLog] sl
where sl.LocationID = l.LocationID)
, i.SiteLogEntry, i.EntryDate, #corrected, i.DATE_CREATED, i.CREATED_BY
from inserted i
left join AQB_MON.[AQB_RMS].[SiteLogType] st on st.SiteLogType = i.SiteLogType
left join AQB_MON.AQB_RMS.Location l on l.SourceSiteID = i.SourceSiteID
END
GO
I believe that you are close.
At this part of the query in the trigger: (I set the columns vertically so that the difference is more noticable)
SELECT st.SiteLogTypeID,
l.LocationID,
(select AQB_RMS.F_GetLogEntryNumber from [AQB_MON].[AQB_RMS].[SiteLog] sl where sl.LocationID = l.LocationID),
i.SiteLogEntry,
i.EntryDate,
#corrected,
i.DATE_CREATED,
i.CREATED_BY
...should be:
SELECT st.SiteLogTypeID,
l.LocationID,
AQB_RMS.F_GetLogEntryNumber(select l.LocationID from [AQB_MON].[AQB_RMS].[SiteLog] sl where sl.LocationID = l.LocationID),
i.SiteLogEntry,
i.EntryDate,
#corrected,
i.DATE_CREATED,
i.CREATED_BY
So basically, you would call the function name with the query as the parameter, which the results thereof should only be one row with a value.
Note that in my modified example, I added the l.LocationID after the select in the function call, so I'm not sure if this is what you need, but change that to match your needs. Because I'm not sure of the exact column that you need, add a comment should there be other issues.
I have a trigger:
ALTER TRIGGER [dbo].[tg_trs_uharian] ON [dbo].[master_st]
AFTER INSERT AS
BEGIN
SET NOCOUNT ON
declare #tgl_mulai varchar(10),
#tgl_selesai varchar(10),
#kdlokasi int,
#thn_harian int,
#date_diff int
declare #tugasID int;
declare #uangharian20 decimal(15,2);
declare #uangharian80 decimal(15,2);
declare #uangharian100 decimal(15,2);
select #tugasID=tugasID
from inserted
SET #thn_harian=CAST(YEAR(CONVERT(datetime, #tgl_mulai, 103)) AS INT);
SET #date_diff=((SELECT datediff(day,CONVERT([datetime],#tgl_mulai,(103)),CONVERT([datetime],#tgl_selesai,(103))))+1);
SET #uangharian100 = (
SELECT k.uh_nominal
FROM master_st m
LEFT OUTER JOIN ref_uharian AS k
ON k.uh_kdlokasi=m.kdlokasi AND k.uh_tahun=#thn_harian);
insert into trs_uangharian (tugasID, uangharian100) values
(#tugasID, #uangharian100);
END
How to make select #tugasID=tugasID from inserted applicable for multiple row inserted row table with different tugasID? It seems that my code is applicable for single row only.
It seems that #date_diff is not used
You use #thn_harian so we need #tgl_mulai, but it is NULL by default
So your INSERT statement has some problems.
I assumed that #tgl_mulai is a column of the original table master_st so I treat it as a column of "inserted" trigger internal table
ALTER TRIGGER [dbo].[tg_trs_uharian] ON [dbo].[master_st]
AFTER INSERT AS
BEGIN
SET NOCOUNT ON
insert into trs_uangharian(tugasID, uangharian100)
select
i.tugasID,
k.uh_nominal
from inserted i
left join ref_uharian AS k
ON k.uh_kdlokasi = i.kdlokasi AND
k.uh_tahun = CAST(YEAR(CONVERT(datetime, i.tgl_mulai, 103)) AS INT)
END
Please, this is a common problem among new SQL developers
SQL triggers work set-based.
Do not calculate any value using variables.
These can only store the last row's calculations in general.
Instead use Inserted and Deleted internal tables.
Your query is messed up a bit, so I can provide only general solution. Change INSERT part on something like this:
INSERT INTO trs_uangharian (tugasID, uangharian100)
SELECT i.tugasID,
k.uh_nominal
FROM inserted i
LEFT JOIN ref_uharian AS k
ON k.uh_kdlokasi=i.kdlokasi AND k.uh_tahun=#thn_harian
You should be able to replace the INSERT statement with this:
INSERT INTO trs_uangharian (tugasID, uangharian100)
SELECT
tugasID,
#uangharian100
FROM
inserted
However it looks like you also have an issue with #tgl_mulai and #tgl_selesai not being set to anything.
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.
I'm using MERGE in my query and i'm making INSERT on clause WHEN NOT MATCHED THEN, but then i would like to get the inserted row identity and make another INSERT to some other table. Query for now is:
ALTER PROCEDURE [dbo].[BulkMergeOffers]
#data ImportDataType READONLY
AS
SET NOCOUNT ON;
DECLARE #cid int = 0
MERGE dbo.oferta AS target
USING #data AS source
ON (target.nr_oferty = source.nr_oferty)
WHEN NOT MATCHED THEN
INSERT (nr_oferty,rynek,typ_transakcji, typ_nieruchomosci,cena,powierzchnia, rok_budowy, wojewodztwo, miasto, dzielnica, ulica, opis, wspolrzedne, film, zrodlo, KontaktStore, data, forma_wlasnosci, stan_techniczny, liczba_pokoi, liczba_peter, pietro, material, kuchnia, pow_dzialki, typ_dzialki, woda,gaz, prad,sila, przeznaczenie,lokal_dane)
VALUES (source.nr_oferty,source.rynek,source.typ_transakcji, source.typ_nieruchomosci,source.cena,source.powierzchnia, source.rok_budowy, source.wojewodztwo, miasto, source.dzielnica, source.ulica, source.opis, source.wspolrzedne, source.film, source.zrodlo, source.KontaktStore, source.data, source.forma_wlasnosci, source.stan_techniczny, source.liczba_pokoi, source.liczba_peter, source.pietro, source.material, source.kuchnia, source.pow_dzialki, source.typ_dzialki, source.woda,source.gaz, source.prad,source.sila, source.przeznaczenie,source.lokal_dane);
So as you see i need to insert some values to the target table based on source data, then i need to take the insert identity and insert it into another table but also based on some source data, so something like that, just after the first insert:
SET #cid = SCOPE_IDENTITY();
if source.photo is not null
begin
insert into dbo.photos(offerID, file) values (#cid, source.photo);
end
But i can't assemble it, a have no access to the source no more, also if statement show error :
"the multi-part identifier
source.photo can not be bound"
but it is there. Just for clarity ImportDataType is a table-valued parameter.
Please HELP
If you don't need the WHEN MATCHED part of the MERGE statement in your query, there's no real reason to use MERGE. You could use INSERT with an outer join or NOT EXISTS statement.
In either case, you can use the OUTPUT clause to retrieve the inserted identity value an pass it on to a second query.
I've extended your example:
<stored procedure header - unchanged>
--declare a table variable to hold the inserted values data
DECLARE #newData TABLE
(nr_oferty int
,newid int
) -- I'm guessing the datatype for both columns
MERGE dbo.oferta AS target
USING #data AS source
ON (target.nr_oferty = source.nr_oferty)
WHEN NOT MATCHED THEN
INSERT (nr_oferty,rynek,typ_transakcji, typ_nieruchomosci,cena,powierzchnia, rok_budowy, wojewodztwo, miasto, dzielnica, ulica, opis, wspolrzedne, film, zrodlo, KontaktStore, data, forma_wlasnosci, stan_techniczny, liczba_pokoi, liczba_peter, pietro, material, kuchnia, pow_dzialki, typ_dzialki, woda,gaz, prad,sila, przeznaczenie,lokal_dane)
VALUES (source.nr_oferty,source.rynek,source.typ_transakcji, source.typ_nieruchomosci,source.cena,source.powierzchnia, source.rok_budowy, source.wojewodztwo, miasto, source.dzielnica, source.ulica, source.opis, source.wspolrzedne, source.film, source.zrodlo, source.KontaktStore, source.data, source.forma_wlasnosci, source.stan_techniczny, source.liczba_pokoi, source.liczba_peter, source.pietro, source.material, source.kuchnia, source.pow_dzialki, source.typ_dzialki, source.woda,source.gaz, source.prad,source.sila, source.przeznaczenie,source.lokal_dane)
OUTPUT inserted.nr_oferty, inserted.<tableId> INTO #newData;
-- replace <tableId> with the name of the identity column in dbo.oftera
insert into dbo.photos(offerID, file)
SELECT nd.newid, pt.photo
FROM #data AS pt
JOIN #newData AS nd
ON nd.nr_oferty = pt.nr_oferty
WHERE pt.photo IS NOT NULL