Modify SQL Trigger to work with BULK INSERT - sql

I have a SQL Trigger that doesn't fire because the records in the table are inserted through a BULK INSERT. I do not have access to the code that inserts the records so I need to modify this trigger to handle the BULK INSERT. This is the trigger:
USE [testdata]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Trigger_test] ON [dbo].[test]
AFTER INSERT , UPDATE
AS
BEGIN
DECLARE #BatchId int, #Ethanol decimal(18,6), #Glucose decimal(18,6), #SampleAge varchar(50);
SELECT #BatchId = CONVERT(int,bd.[BatchId]),
#Ethanol = CONVERT(decimal(18,2),[Ethanol]),
#Glucose= CONVERT(decimal(18,2),[Glucose]),
#SampleAge = bd.SampleCode
from INSERTED bd
update [dbo].[DeSchedule]
SET
[Ethanol] = #Ethanol,
[Glucose] = #Glucose,
[SampleCompleted] = 1
WHERE [BatchID] = #BatchId AND [SampleAge] = #SampleAge
END
Can anyone help me in modifying this trigger to handle the BULK INSERT.

Unless you can modify the BULK INSERT statement you are stuck. By default triggers do NOT run during a bulk insert. You must explicitly turn them on in the command with the FIRE_TRIGGER option.

Only need to edit BULK INSERT File below:
BULK INSERT AdventureWorks2012.Sales.SalesOrderDetail
FROM 'f:\orders\lineitem.tbl'
WITH
(
FIELDTERMINATOR =' |'
, ROWTERMINATOR = ':\n'
, FIRE_TRIGGERS
);

USE [testdata]
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
ALTER TRIGGER [dbo].[Trigger_test] ON [dbo].[test]
AFTER INSERT , UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #BatchId int, #Ethanol decimal(18,6), #Glucose decimal(18,6), #SampleAge varchar(50);
Declare CurI cursor for
SELECT [BatchId],[Ethanol] ,[Glucose], SampleCode from INSERTED
Open CurI
fetch next from CurI into #BatchId,#Ethanol,#Glucose, #SampleAge
while ##fetch_status=0
Begin
update [dbo].[DeSchedule]
SET
[Ethanol] = #Ethanol,
[Glucose] = #Glucose,
[SampleCompleted] = 1
WHERE [BatchID] = #BatchId AND [SampleAge] = #SampleAge
Fetch next from CurI into #BatchId,#Ethanol,#Glucose, #SampleAge
End
Close CurI
Deallocate CurI
END

the problem is it selected only the last row of the inserted table ,i think if you change the query like this it would work
update [dbo].[DeSchedule]
SET
[Ethanol] =(select CONVERT(int,bd.[Ethanol]) from inserted bd),
[Glucose] = (select CONVERT(decimal(18,2),[Glucose]) from inserted bd),
[SampleCompleted] = 1
WHERE [BatchID] = (select CONVERT(int,bd.[BatchId]) from inserted bd) AND [SampleAge] = (select bd.SampleCode from inserted bd)

Related

Stored procedure. Check if constraint are used in another table

I get this error message:
The DELETE statement conflicted with the REFERENCE constraint
"FK_FieldMapper_Field". The conflict occurred in database "SCAM",
table "dbo.FieldMapper", column 'FieldID'.
I have some accesshelpers which has a given amount of fields. These Fields can be used by multiple accesshelpers..
When I delete an accesshelper, I need to check if the fields in the given accesshelper are used by other accesshelpers. If they are, I shall delete the accesshelper, but not the fields, as that would break other accesshelpers.
How do i do that?
This is what I have come up with on my own so far.
USE [SCAM]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[deleteAccessHelperById]
#Id int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #projectIds TABLE (id int);
INSERT INTO #projectIds ([id]) SELECT dbo.AccessHelperMapper.ProjectID FROM dbo.AccessHelperMapper WHERE dbo.AccessHelperMapper.AccessHelperID = #Id;
DELETE dbo.AccessHelperMapper WHERE dbo.AccessHelperMapper.AccessHelperID = #Id;
DECLARE #fieldIds TABLE (id int);
INSERT INTO #fieldIds ([id]) SELECT dbo.FieldMapper.FieldID FROM dbo.FieldMapper WHERE dbo.FieldMapper.AccessHelperID = #Id;
DECLARE #AHfields TABLE (id int)
Insert into #AHfields
Select fids.id from dbo.FieldMapper, #fieldIds as fids
where dbo.FieldMapper.AccessHelperID != #Id
and fids.id != dbo.FieldMapper.FieldID;
delete dbo.FieldMapper where dbo.FieldMapper.AccessHelperID = #Id and dbo.FieldMapper.FieldID IN (SELECT d.id FROM #AHfields as d);
delete dbo.Field where dbo.Field.ID in (SELECT g.id FROM #AHfields as g);
delete dbo.AccessHelper where dbo.AccessHelper.ID = #Id;
END
Try the below code. Hope this is what you were looking for:
USE [SCAM]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[deleteAccessHelperById]
#Id int
AS
BEGIN
SET NOCOUNT ON;
DELETE dbo.AccessHelperMapper WHERE dbo.AccessHelperMapper.AccessHelperID = #Id;
DECLARE #fieldIds TABLE (id int);
INSERT INTO #fieldIds ([id]) SELECT dbo.FieldMapper.FieldID FROM dbo.FieldMapper WHERE dbo.FieldMapper.AccessHelperID = #Id
AND dbo.FieldMapper.FieldID NOT IN (SELECT DISTINCT dbo.FieldMapper.FieldID FROM dbo.FieldMapper WHERE dbo.FieldMapper.AccessHelperID != #Id);
delete dbo.FieldMapper where dbo.FieldMapper.AccessHelperID = #Id --and dbo.FieldMapper.FieldID IN (SELECT d.id FROM #fieldIds as d);
delete dbo.Field where dbo.Field.ID in (SELECT g.id FROM #fieldIds as g);
delete dbo.AccessHelper where dbo.AccessHelper.ID = #Id;
END
First of all you need a transaction because you have partial data changes which cannot be applied separately.
ALTER PROCEDURE [dbo].[deleteAccessHelperById]
#Id int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #fieldIds TABLE (id int);
BEGIN TRY
BEGIN TRAN
DELETE ahm
FROM dbo.AccessHelperMapper ahm
WHERE ahm.AccessHelperID = #Id;
DELETE fm
OUTPUT DELETED.fieldID
INTO #fieldIds(id)
FROM dbo.FieldMapper fm
WHERE fm.AccessHelperID = #Id
DELETE f
FROM dbo.Field f
WHERE exists(select 1 from #fieldIDs fid where fid.ID = f.ID)
and not exists(
select 1 from dbo.FieldMapper fm
where fm.fieldID = f.ID
and fm.AccessHelperID != #ID -- redundant, may be removed.
)
DELETE ah
FROM dbo.AccessHelper ah
where ah.ID = #Id
COMMIT TRAN
END TRY
BEGIN CATCH
IF XACT_STATE() IN (1, -1)
ROLLBACK TRAN
THROW;
END CATCH
END

Separate rows in multiple delete SQL server

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

Audit table update trigger does not work using a stored procedure

I have table tblA
Which has a trigger defined on it, if any rows are updated it writes the changes to an audit table.
Now if on SQL Server I right click and edit a row directly, I see changes go to the audit table.
If I call a stored procedure to do an update, I do see tblA updated but the changed values do not go into the audit table. It seems like the trigger does not get fired at all.
What difference is there between directly editing a table or updating through a stored procedurein term of the trigger being fired.
Trigger
USE [dbSuppHousing]
GO
/****** Object: Trigger [dbo].[tblSurvey_trigger] Script Date: 9/22/2015 2:32:51 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/* =============================================
Create date: 08/27/15
Description: Trigger to Add audit records to audtSurvey
// =============================================*/
ALTER TRIGGER [dbo].[tblSurvey_trigger] on [dbo].[tblSurvey]
FOR UPDATE,DELETE
AS
SET NOCOUNT ON
Declare #FieldName varchar(100)
, #Deleted varchar(4000)
, #Inserted varchar(4000)
, #SurveyID numeric
, #LoginName varchar(100)
Declare #currentDate datetime = null
SET #currentDate = getdate()
Set #Deleted = ''
Set #Inserted = ''
Select * into #Deleted From Deleted
Select * into #Inserted From Inserted
Create Table #DT ([NameValue] varchar(1000))
Create Table #IT ([NameValue] varchar(1000))
Begin Transaction
Declare auSurveyCur cursor for Select Survey_ID from #Deleted Order By Survey_ID
open auSurveyCur
fetch next from auSurveyCur into #SurveyID
while ##fetch_status = 0
Begin
Declare auU cursor
for Select [name] from syscolumns where id = object_id('dbo.tblSurvey')
open auU
fetch next from auU into #FieldName
while ##fetch_status = 0
Begin
Insert into #DT execute ('Select ' + #FieldName + ' From #Deleted Where Survey_ID = ' + #SurveyID)
Insert into #IT execute ('Select ' + #FieldName + ' From #Inserted Where Survey_ID = ' + #SurveyID)
Set #Deleted = (Select isnull(NameValue,'--') From #DT)
Set #Inserted = (Select isnull(NameValue,'--') From #IT)
If (#Deleted <> #Inserted)
Begin
SELECT #LoginName=Login_Name
From Inserted Where Survey_ID=#SurveyID
if #Deleted = '--' begin set #Deleted = null end
if #Inserted = '--' begin set #Inserted = null end
--#ForWhat=1 means info is for tbSurvey
--In future there may be more tables for auditing and we use same
--Stored procedure to insert audit. Each table has its own audit table.
Execute dbo.InsertauInfo #Surv_ID=#SurveyID
, #auSurveyFieldName=#FieldName
, #auSurveyChangedFrom=#Deleted
, #auSurveyChangedTo=#Inserted
, #auSurveyUpdatedBy=#LoginName
, #auSurveyUpdateDate=#currentDate
, #ForWhat=1
End
Delete From #DT
Delete From #IT
fetch next from auU into #FieldName
End
close auU
deallocate auU
fetch next from auSurveyCur into #SurveyID
END
close auSurveyCur
deallocate auSurveyCur
Commit Transaction
Code for InsertauInfo
ALTER PROCEDURE [dbo].[InsertauInfo]
#Surv_ID bigint,
#auSurveyFieldName varchar(100),
#auSurveyChangedFrom varchar(max),
#auSurveyChangedTo varchar(max),
#auSurveyUpdatedBy varchar(100),
#auSurveyUpdateDate datetime,
#ForWhat int
AS
BEGIN
SET NOCOUNT ON;
IF (#ForWhat=1)
BEGIN
INSERT INTO auSurvey
( Survey_ID
, auSurveyFieldName
, auSurveyChangedFrom
, auSurveyChangedTo
, auSurveyUpdateDate
, auSurveyUpdatedBy
)
VALUES
( #Surv_ID
, #auSurveyFieldName
, #auSurveyChangedFrom
, #auSurveyChangedTo
, #auSurveyUpdateDate
, #auSurveyUpdatedBy
)
END
--ELSE IF (#ForWhat=2)
-- BEGIN
-- INSERT INTO ABC
-- END
END
Example OF stored procedure which does not cause trigger to fire:
OK I'm going crazy but if I Run this very simpple stored procedure directly in DB tirgger gets fired. But if I run from my C# web, Stored procedure will run since tblSurvey gets updated but trigger wont get fired.
ALTER PROCEDURE [dbo].[spUpdateSurveyDataTest]
AS
BEGIN
Update tblSurvey
Set
[s1FirstName]='YES TRIGGER WORKS for sureYES'
Where Survey_Id=327
END
Your trigger only fires on updates and deletes which is why it fired when you edited the table. The stored procedure is performing an insert which is undetected by your trigger. Your trigger should be for INSERT,UPDATE,DELETE.
The point of matter was as I mentioned in the question:
Trigger could not be run from App but I was able to fire it directly from a database update.
The problem was application was connecting to DB with its own generic User which did not have Execute permission for Trigger.
To make things more complicated there was no exception at application level.
The fix was to add
WITH EXECUTE AS OWNER
To the trigger.

Storedprocedure to check the value already in the table

I have a stored procedure to insert values in to a table.Here I need to check the values for insert is already in the table .how can I check this in my stored procedure.here is my stored procedure.
USE [Databasse_sync]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
RETURN
Here I need to check the ProducId is same or not?If ProductId is same then Update otherwise Insert.>>>??
USE [Databasse_sync]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
MERGE [YoutubeVideo] AS Y
USING (SELECT #ProductID,#YoutubeUrl, #YoutubeImage) AS SourceRow (ProductID,YoutubeUrl,YoutubeImage)
ON Y.[ProductID] = SourceRow.ProductID
WHEN MATCHED THEN
UPDATE SET
Y.[ProductID] = SourceRow.ProductID
,Y.[YoutubeUrl] = SourceRow.YoutubeUrl
,Y.[YoutubeImage] = SourceRow.YoutubeImage
WHEN NOT MATCHED THEN
INSERT (ProductID,YoutubeUrl, YoutubeImage, DATASET)
VALUES (SourceRow.ProductID,SourceRow.YoutubeUrl, SourceRow.YoutubeImage,'DAT');
RETURN
IF NOT EXISTS(SELECT TOP 1 ProductID FROM YoutubeVideo WHERE ProductID=#ProductID) BEGIN
--INSERT HERE
END
ELSE BEGIN
--UPDATE HERE
END
DECLARE #EXISTS BIT
SELECT #EXISTS = 1
WHERE EXISTS
(SELECT ID FROM YouTubeVideo
WHERE ID = #ProductID
AND YoutubeUrl = #YoutubeUrl)
IF #Exists = 1
BEGIN
-- Update
END
ELSE
BEGIN
-- INSERT
END
#Arun
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS
BEGIN
DECLARE #counter INT
#counter=SELECT COUNT(ProductID) FROM YoutubeVideo WHERE ProductID=#ProductID
IF(#counter = 0)
BEGIN
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
END
END
if u want to check record is inserted or not then u can take one out parameter and chek recored is inserted or not.
try this:
ALTER PROCEDURE [dbo].[kt_getyoutubevideo]
#ProductID nvarchar(200),
#YoutubeUrl nvarchar(200),
#YoutubeImage nvarchar(200)
AS BEGIN
DECLARE #counter int
#counter = SELECT count(ProductID) FROM YoutubeVideo WHERE ProductID=#ProductID
IF(#counter = 0)
BEGIN
INSERT INTO YoutubeVideo(ProductID,YoutubeUrl,YoutubeImage,DATASET)VALUES(#ProductID,#YoutubeUrl,#YoutubeImage,'DAT')
End
ELSE
begin
UPDATE SET
ProductID = #ProductID
,YoutubeUrl = #YoutubeUrl
,YoutubeImage = #YoutubeImage
END END

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