Separate rows in multiple delete SQL server - sql

I have a trigger. When i delete something in my table i want to create a row in other table named Archive with the row i deleted. All works fine. The problem is when i delete more than one row in the same time. It store just the first row in the table Archive. I think about creating a loop but how i separate them in subqueries to select every row. How do i solve that?
ALTER TRIGGER [dbo].[trigArhiva]
ON [dbo].[student]
AFTER DELETE
AS
SET NOCOUNT ON;
BEGIN
declare #nume varchar(45);
declare #anStudiu int;
declare #prenume varchar(45);
declare #CNP char(13);
declare #grupa int;
declare #idFacult int;
declare #rowss int;
declare #i int;
select #rowss = count(*) from DELETED;
set #i = 1;
while (#i <= #rowss)
begin
select #nume = nume from DELETED;
select #anStudiu = anStudiu from DELETED;
select #prenume = prenume from DELETED;
select #CNP = CNP from DELETED;
select #grupa = idGrupa from DELETED;
select #idFacult = idFacult from DELETED;
insert into arhivaStud(nume, prenume, CNP, grupa, idFacult, anStudiu) values (#nume, #prenume, #CNP, #grupa, #idFacult, #anStudiu);
set #i = #i+1;
end
END

I think you should use something like this (deleted is an "internal" representation of all records deleted during the operation which recall the trigger):
ALTER TRIGGER [dbo].[trigArhiva]
ON [dbo].[student]
AFTER DELETE
AS
SET NOCOUNT ON;
BEGIN
INSERT INTO arhivaStud(nume, prenume, CNP, grupa, idFacult, anStudiu)
SELECT nume, prenume, CNP, idgrupa, idFacult, anStudiu FROM deleted
END

Related

Updating Null records of a table by invoking stored procedure throws error 'Subquery returned more than one value'

I am trying to update all null values of a column with Uuid (generated with the help of a stored procedure GetOptimizedUuid). While doing so I am getting an error
Subquery returned more than 1 value
I could understand the causes of error but none of my fix helped out.
I tried out with some loops but it doesn't fix
BEGIN
DECLARE #no INT;
DECLARE #i INT;
SET #no = (SELECT COUNT(id) FROM table1)
SET #i = 0;
WHILE #i < #no
BEGIN
DECLARE #TempUuid TABLE(SeqUuid UNIQUEIDENTIFIER, OptimizedUuid UNIQUEIDENTIFIER)
INSERT INTO #TempUuid
EXECUTE [Sample].[dbo].[GetOptimizedUuid]
UPDATE table1
SET col2 = (SELECT OptimizedUuid FROM #TempUuid)
WHERE col2 IS NULL;
SET #i = #i + 1;
END
END
Help me to sort out this, Thanks!
Not entirely sure what you're doing - what do you need to call this GetOptimizedUuid stored procedure? Can't you just use NEWID() to get a new GUID?
Anyway - assuming you have to call this stored procedure, I assume you'd call it once before the loop, to get the ID's you need - and then you get the top (1) UUID from the table and update one row in your database table - and then you also need to remove that UUID that you've just used from the temp table, otherwise you keep re-using the same ID over and over again....
Try something like this:
BEGIN
DECLARE #no INT;
DECLARE #i INT;
SET #no = (SELECT COUNT(id) FROM table1)
SET #i = 0;
-- define and fill the table *ONCE* and *BEFORE* the loop
DECLARE #TempUuid TABLE(SeqUuid UNIQUEIDENTIFIER, OptimizedUuid UNIQUEIDENTIFIER)
INSERT INTO #TempUuid
EXECUTE [Sample].[dbo].[GetOptimizedUuid]
-- declare a UUID to use
DECLARE #NewUuid UNIQUEIDENTIFIER;
WHILE #i < #no
BEGIN
-- get the first UUID from the temp table
SELECT TOP (1) #NewUuid = OptimizedUuid
FROM #TempUuid;
-- update your table
UPDATE table1
SET col2 = #NewUuid
WHERE col2 IS NULL;
-- *REMOVE* that UUID that you've used from the table
DELETE FROM #TempUuid
WHERE OptimizedUuid = #NewUuid;
SET #i = #i + 1;
END
END

SQL Server trigger with loop for multiple row insertion

I've created trigger for my database which handles some insertion but when I add multiple values in 1 SQL query it doesn't work:
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
DECLARE #ID INT
DECLARE #dayC INT
DECLARE #counter INT
SET #counter = 1
SET #ID = (SELECT IDConference FROM Inserted)
SET #dayC = (SELECT DATEDIFF(DAY, start,finish) FROM Inserted)
WHILE #counter <= #dayC + 1
BEGIN
EXEC AddConferenceDay #Id, #counter
SET #counter = #counter +1
END
END
For single insertion it works ok. But what should I change/add to make it execute for each row of inserted values?
If you cannot change the stored procedure, then this might be one of the (very few) cases when a cursor comes to the rescue. Double loops, in fact:
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
DECLARE #ID INT;
DECLARE #dayC INT;
DECLARE #counter INT
SET #counter = 1;
DECLARE yucky_Cursor CURSOR FOR
SELECT IDConference, DATEDIFF(DAY, start,finish) FROM Inserted;
OPEN yucky_Cursor; /*Open cursor for reading*/
FETCH NEXT FROM yucky_Cursor INTO #ID, #dayC;
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE #counter <= #dayC + 1
BEGIN
EXEC AddConferenceDay #Id, #counter;
SET #counter = #counter + 1;
END;
FETCH NEXT FROM yucky_Cursor INTO #ID, #dayC;
END;
CLOSE yucky_Cursor;
DEALLOCATE yucky_Cursor;
END;
I suspect there is a way to refactor and get rid of the cursor and use set-based operations.
When you insert more than one record, you need to cursor/while to call the AddConferenceDay procedure for each record.
But I will suggest you to alter your procedure to accept table type as input parameter. So that more than one ID and dayC as input to AddConferenceDay procedure. It is more efficient than your current approach.
something like this
create type udt_Conferences as table (ID int,dayC int)
Alter the procedure to use udt_Conferences as input parameter
Alter procedure AddConferenceDay (#input udt_Conferences readonly)
as
begin
/* use #input table type instead of #Id and #counter variables */
end
To call the procedure update the trigger with created udt
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
Declare #input udt_Conferences
insert into #input (ID,dayC)
select IDConference,DATEDIFF(DAY, start,finish) from Inserted
END
add these lines to your trigger
AFTER INSERT
AS
BEGIN
AFTER INSERT
AS
BEGIN
Declare #Count int;
Set #Count=##ROWCOUNT;
IF #Count=0
Return;
SET NOCOUNT ON;
-- Insert statements for trigger here

update trigger get the updated primary key

I have a trigger ,but I need to get the updated record's primary key (like as inserting the data SELECT #Id= ##IDENTITY) thus, I can pass it to where condition. How can I do that?
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar]
ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
--how I can I get the last updateed record Identity like this??
--and pass it to update query as a where condition
SELECT #Id= ##IDENTITY
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
END
As Martin said, you have to understand that SQL Server triggers are per statement, not per row. So in context of your trigger you have two tables - inserted and deleted, where you could find all information about data updated. If you really want to do per row processing, you could use cursor:
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar] ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
declare tr_cursor cursor local fast_forward for
select ID from inserted
while 1 = 1
begin
fetch tr_cursor into #Id
if ##fetch_status <> 0 break
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
end
close tr_cursor
deallocate tr_cursor
END

Suppress result from insert trigger

A MS SQL insert trigger causes some grief as it returns a result. Is there a way to disable any result from being sent? I've used 'set nocount on', but that doesn't seem to do much...
create trigger tr_insert_raw_data
on raw_data
instead of insert
as
begin
set nocount on
declare #query varchar(max);
declare #rev int;
declare #id int;
declare #oldrev int;
declare #contextinfo VARBINARY(128)
select #contextinfo = context_info()
if #contextinfo = 0x00001
begin
insert into raw_data select * from inserted
return
end
select * into #query from inserted
set #id = (select count(distinct id) from raw_data);
set #id += 1;
insert into revisions (username, hostname, ts) values (SYSTEM_USER, HOST_NAME(), GETDATE())
set #rev = (select max(id) from revisions);
select #oldrev = revision_id from inserted;
if #oldrev is null set #oldrev=#rev-1;
select * into #inserted from inserted
update #inserted set revision_id = #rev, id = #id
select * from #inserted
insert into raw_data select * from #inserted
insert into edges (a, b) values (#oldrev, #rev)
end
what kind of a result are you getting?
After a quick review of your query my first thought is that you're getting results due to the
select * from #inserted
line you have (third line from bottom). This line tells me that you are requesting results which is probably why you're getting some?
If you do need values selected from a table to modify or custom select out of, perhaps CTE's will help you out? (Common Table Expressions)

Is there a way to force a trigger to run on an update statement with multiple rows?

I have had to make changes to a trigger and assumed that running an update query like the following would make the trigger execute for all the matched rows. But instead, it only updates the record that it finds.
UPDATE someTable SET someField = someField WHERE someField = 'something';
As a quick solution, I created the following query using a cursor to loop through the records and update each row. It works, and luckily I don't have a really large dataset so it doens't take too long, but it just doesn't seem like the best solution.
DECLARE #id INT;
DECLARE queryCursor CURSOR FOR
SELECT id FROM someTable WHERE someField='something'
OPEN queryCursor
FETCH NEXT FROM queryCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE someTable SET someField = someField WHERE id = #id
FETCH NEXT FROM queryCursor INTO #id
END
CLOSE queryCursor
DEALLOCATE queryCursor
Is there a better way to get a trigger to execute on multiple rows in SQL Server?
Edit: The code from trigger
FOR INSERT, UPDATE
AS
IF UPDATE (LineNumber)
OR UPDATE(LineService)
Begin
DECLARE #CDL VARCHAR(50)
DECLARE #LN VARCHAR(100)
DECLARE #A VARCHAR(25)
SELECT #CDL = CommonDataLink FROM INSERTED
SELECT #A = LineService FROM INSERTED
SET #LN = #CDL + #A
UPDATE CommonData SET ReportedLineNo = #LN WHERE CommonDataLink = #CDL
End
You have to make use of the special table INSERTED for what you want:
UPDATED CODE
FOR INSERT, UPDATE
AS
IF UPDATE (LineNumber)
OR UPDATE(LineService)
Begin
DECLARE #CDL VARCHAR(50)
DECLARE #LN VARCHAR(100)
DECLARE #A VARCHAR(25)
SELECT #CDL = CommonDataLink FROM INSERTED
SELECT #A = LineService FROM INSERTED
SET #LN = #CDL + #A
UPDATE A
SET ReportedLineNo = B.LineService + B.CommonDataLink
FROM CommonData A
INNER JOIN INSERTED B
ON A.CommonDataLink = B.CommonDataLink
End