IF statement with SQL Server trigger - sql

I am a beginner with SQL and I'm trying to create a trigger to fire with an IF statement. Below is the trigger:
CREATE TRIGGER [Vector].[trg_insert_id]
ON [vector].[a69]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #OID int
SELECT #OID = OBJECTID FROM INSERTED
DECLARE #siteID float
SELECT #siteID = MAX([SiteID]) FROM [vector].[a69]
SELECT #siteID
IF #OID NOT IN(select OBJECTID from vector.a69)
BEGIN
UPDATE [vector].[a69]
SET SiteID = #siteID + 0.00001
WHERE OBJECTID IN (#OID)
END
END
What I'm trying to have the trigger do is, after an insert to the table, search to see if the inserted OBJECTID already exists in the table. If it does not, update SiteID field by 0.00001.
When I have the IF statement, the trigger does not work. If I remove the IF statement, the trigger works, but it increments the SiteID field for other records with the same OBJECTID.
Any suggestions on what I'm doing wrong?

NOT IN can be dangerous to use. If the subquery returns even a single NULL value, then the results is never true. This may be the cause of your problem. One simple fix is:
IF #OID NOT IN (select OBJECTID from vector.a69 where OBJECTID is not null)
. . .
I think a better approach is to use NOT EXISTS:
IF NOT EXISTS (select 1 from vector.a69 where OBJECTID = #OID)

Might as well learn to use triggers correctly. First, if you ever find yourself writing something like:
DECLARE #OID int
SELECT #OID = OBJECTID FROM INSERTED
then your trigger is broken . It must be able to handle multiple values in the inserted or deleted pseudo-tables:
Next this part doesn't make logical sense
IF #OID NOT IN(select OBJECTID from vector.a69)
BEGIN
UPDATE [vector].[a69]
SET SiteID = #siteID + 0.00001
WHERE OBJECTID IN (#OID)
END
IF #OID doesn't exist in table vector.a60 then you can't update it. I am partly guessing what you want to do but I think this is what you actually want in the trigger.
CREATE TRIGGER [Vector].[trg_insert_id]
ON [vector].[a69]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [vector].[a69]
SET SiteID = #siteID + 0.00001
WHERE OBJECTID IN (SLECT OID FROM inserted)
END

Related

Unable to get computed value with After Insert trigger

I'm using the FileTables feature and have setup a trigger to populate a FileProperties table whenever a file is copied to the SQL Server file share.
I'm trying to grab the file size (cached_file_size), and I get a value of zero. I set up the trigger the same way a standard FileTable does using DATALENTGH(file_stream). The code below is using the inserted tables' file_stream:
ALTER TRIGGER [dbo].[FileTable_Insert_Trigger]
ON [dbo].[Files]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #s_id UNIQUEIDENTIFIER,
#fs VARBINARY(MAX),
#nm NVARCHAR(255),
#fp NVARCHAR(MAX),
#cfs BIGINT,
#ft NVARCHAR(255);
SELECT #s_id = ins.stream_id FROM inserted ins;
SELECT #fs = ins.file_stream FROM inserted ins;
SELECT #nm = ins.name FROM inserted ins;
SELECT #fp = ins.file_stream.GetFileNamespacePath() FROM inserted ins;
SELECT #ft = ins.file_type FROM inserted ins;
SELECT #cfs = DATALENGTH(#fs);
INSERT INTO [FileProperties] (stream_id, [name], filepath, file_type,
cached_file_size, DateAdded, UserID)
VALUES (#s_id, #nm, #fp, #ft,
#cfs, CURRENT_TIMESTAMP, 1);
END
I've also tried grabbing the computed value directly from the FileTable:
SELECT #cfs = Files_1.cached_file_size
FROM [dbo].Files AS Files_1
WHERE Files_1.stream_id = #s_id;
Still getting zero. What am I missing here? Thank you.
[EDIT] I tried playing around with getting the DATALENGTH of other values such as the name (#nm) and stream_id (#s_id) and I do get the values correctly.

Trigger not calling for huge rows insert

I have one table which consists of one trigger which will be called if any insert or update operation performed on that table.
This trigger will insert a new row in other physical table.
First I am taking the entire data to be inserted into a temporary table and then I am inserting data into my physical table(which has trigger).
After performing insert operation all the records in the temporary table are getting inserted into physical table but the trigger is executing for only first record, for rest of the records it is not executing.
Can anyone please help me with this issue.
NOTE : With cursor it is working fine but for performance issue I don't want to use cursor.
ALTER TRIGGER [dbo].[MY_TRG]
ON [dbo].[T_EMP_DETAILS]
FOR INSERT , UPDATE
AS
BEGIN
IF UPDATE(S_EMPLOYEE_ID)OR UPDATE(S_GRADE_ID)OR UPDATE(D_EFFECTIVE_DATE) OR UPDATE(S_EMPLOYEE_STATUS)
BEGIN
DECLARE #EmpId varchar(6)
DECLARE #HeaderId Int
DECLARE #FYStartYear varchar(4)
DECLARE #EffDate Smalldatetime
DECLARE #UpdatedBy varchar(10)
DECLARE #ActionType varchar(1)
DECLARE #RowCount Int
DECLARE #EmpRowCount Int
DECLARE #AuditRowsCount Int
DECLARE #EMP_STATUS VARCHAR(1)
DECLARE #D_FIN_START_YEAR DATETIME
DECLARE #Food_Count int
SELECT #FYStartYear = CAST(YEAR(D_CURRENT_FY_ST_DATE)AS VARCHAR) FROM dbo.APPLICATION WHERE B_IS_CURRENT_FY = 1
SELECT #UpdatedBy = 'SHARDUL'
select #EmpId = S_EMPLOYEE_ID from inserted
select #HeaderId = N_HEADER_TXN_ID from inserted
select #EffDate = D_EFFECTIVE_DATE from inserted
select #FLEXI_AMT = N_FLEX_BASKET_AMT from inserted
select #EMP_STATUS = S_EMPLOYEE_STATUS from inserted
select #D_FIN_START_YEAR=D_FIN_START_DATE from inserted
SELECT #RowCount = count(*) from T_EMP_DETAILS
WHERE S_EMPLOYEE_ID = #EmpId and
SUBSTRING(CAST(D_EFFECTIVE_DATE AS VARCHAR),1,11) = SUBSTRING(CAST(#EffDate AS VARCHAR),1,11)
BEGIN
exec INSERT_DEFAULT_VALUES #EmpId,#HeaderId,#UpdatedBy
END
That's one of many reasons Bulk is so fast :). Read Bulk Insert syntax and you'll see FIRE_TRIGGERS parameter. Use it.
As I wrote in my comment - you are using inserted in improper way. As written now it will work only for 1 row.
The second one is a WEIRD number of variables, and only few are used, why?
Third - you are using SP in the end of batch, you need to post it's code, I bet there is some insert in it, maybe you could avoid using this SP and insert directly in some table from inserted.

Using Trigger to keep data integrity

I have scoured the internet for a solution (mostly scouring stack overflow) and I cannot come up with anything.
Here is my goal: I have a local database and I have set up a linked server to another database. I am creating a trigger on one of my local tables. One of the column values is a Hotel ID. In the linked server there is a table called "Hotel". The point of this trigger is to check and make sure that the HotelID I am trying to insert into my local table is a value that exists in the linked server's Hotel table.
Example: If I want to insert a new row into my "Store Table" from local, I want to make sure that the HotelID I am trying to insert exists in the "Hotel" table in my linked server. If it does not exist, I want to rollback the transaction and display a message.
Below is the code I have been playing with. I feel like I could be close, but I am open to the idea that I am extremely far away.
FYI: The code inside of the IF NOT EXISTS statement is incorrect. I am just confused as to what needs to go in there.
CREATE TRIGGER tr_trigger ON Store
AFTER Insert
AS
DECLARE #HotelID smallint = (SELECT HotelID FROM inserted)
DECLARE #query NVARCHAR(MAX) = N'SELECT * FROM OPENQUERY (test,''
SELECT HotelID FROM test.dbo.Hotel WHERE HotelID = ''''' +
CONVERT(nvarchar(15),#HotelID) +''''''')'
DECLARE #StoredResult Nvarchar(20)
BEGIN
EXEC sp_executesql #query, N'#StoredResult NVARCHAR(MAX) OUTPUT', #StoredResult =
#StoredResult OUTPUT
SELECT #StoredResult
IF NOT EXISTS (SELECT * FROM OPENQUERY (test,' SELECT HotelID FROM test.dbo.Hotel'))
BEGIN
PRINT'That HotelID does not exist. Please try again.'
ROLLBACK TRANSACTION
END
END
GO
EDIT: This has been solved thanks to a couple of suggestions from marc_s. Below is my new code that works how I need it to.
CREATE TRIGGER tr_trigger ON Store
AFTER Update, Insert
AS
BEGIN
IF NOT EXISTS (SELECT A.* FROM OPENQUERY (test, 'SELECT HotelID FROM test.dbo.hotel') A
INNER JOIN inserted i
ON A.HotelID = i.HotelID)
BEGIN
PRINT'Please enter a valid HotelID'
ROLLBACK TRANSACTION
END
END
GO
How about:
CREATE TRIGGER tr_DataIntegrity ON Store
AFTER Update, Insert
AS
BEGIN
IF EXISTS (
SELECT * FROM inserted i
WHERE NOT EXISTS (
SELECT A.*
FROM OPENQUERY (TITAN_Prescott_Store, 'SELECT HotelID FROM FARMS_Prescott.dbo.hotel') A
WHERE A.HotelID = i.HotelID))
BEGIN
PRINT'Please do not enter an invalid HotelID'
ROLLBACK TRANSACTION
END
END
GO

How to get sql statement in trigger

I have insert, update, delete triggers for every tables to logging actions.
I am retrieving before and after datas from deleted, inserted and wrapping these into xml.
But some logs can't show before and update values.
My sql statement is:
USE [cop]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Delete] ON [dbo].[Seanslar]
AFTER DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #deleted AS XML
SET #deleted = (select * from deleted for xml AUTO, ELEMENTS XSINIL)
DECLARE #logIslem TINYINT
SET #logIslem = 3
DECLARE #tableName VARCHAR(200)
SELECT #tableName = OBJECT_SCHEMA_NAME( parent_id ) + '.' + OBJECT_NAME( parent_id )
FROM sys.triggers
WHERE object_id = ##PROCID
DECLARE #xmlToChar NVARCHAR(MAX)
SET #xmlToChar = CAST(#deleted AS nvarchar(MAX))
IF LEN(#xmlToChar)<10
BEGIN
IF EXISTS(select * from deleted)
select #xmlToChar = CAST(seans_id AS NVARCHAR(MAX)) from deleted
ELSE
SET #xmlToChar = 'Deleted is empty!'
END
DECLARE #allXml AS XML
SET #allXml = '<'+#tableName+'>'+ #xmlToChar +'</'+#tableName+'>'
INSERT INTO [dbo].[Logla]
([logIslem], [trgKullanici_id], [tabloAdi], [logXml])
VALUES
(#logIslem, SUSER_NAME(), #tableName, #allXml)
END
Is there any way to learn "sql statement" executed inside trigger?
There is no practical way to capture the executing SQL statement text inside of a DML Trigger fired by that statement.
You can do this with a DDL (metadata) Trigger, but not a DML (normal) Trigger.
And yes, there are one or two very impractical ways to do it, but I really do not recommend them unless:
You are very, very SQL proficient, and
You really, really need to get it, and
You can afford a lot of development and testing time

SQL Select or Insert return ID

Alright so a quick SQL question here(using sql-server-2008).
I have a mapping table names with the following columns
ID DisplayName
What I want to do is first
SELECT [ID] FROM [names] WHERE [DisplayName] = 'chuck';
BUT, if the name 'chuck' doesn't exist in the database, I would like to create it, and return the auto incremented ID.
I was wondering if SQL had some built in way of doing this in an easy way, or if I have to go the long route?
long route being something like this
SELECT COUNT(ID) AS count, ID FROM names WHERE DisplayName='chuck'
IF(count > 0)
SELECT ID as ReturnID;
ELSE
BEGIN
INSERT INTO names(DisplayName) values('chuck');
SELECT scope_identity() as ReturnID;
END
I didn't test that last statement, but I assume the long way would be something like that. If there is no built in way, I'd appreciate it if someone could simply correct that statement(as I'm sure it isn't completely correct).
You should take care about transactions as well:
set XACT_ABORT on
begin tran
declare #ID int
select #ID = ID from names with (holdlock, updlock) WHERE DisplayName='chuck'
if ##rowcount = 0
begin
INSERT INTO names(DisplayName) values('chuck');
set #ID = scope_identity();
end
select #ID as ReturnID;
commit tran
Note the usage of table hints - holdlock and updlock. They prevent another thread from executing exactly the same query and creating the row a second time. For more information look for isolation, synchronization, deadlocks, concurrent updates.
I would do:
IF (NOT EXISTS(SELECT null from names where DisplayName='Chuck'))
INSERT INTO Names (DisplayName) Values ('Chuck')
SELECT ID as ReturnID FROM Names where DisplayName='Chuck'
Doesn't save much though
go
create table names
(
Id int primary key identity(1,1),
displayname varchar(100),
);
go
create procedure P1
#displayname varchar(100)
as
insert into names (displayname)
select #displayname
where not exists (
select * from names, (select #displayname as displayname) as names2
where names.displayname = names2.displayname);
-- race condition is possible here,
-- but in some cases you still may get away with this
select id from names where displayname = #displayname;
go
declare #dn varchar(100);
set #dn = 'chuck'; exec P1 #dn; exec P1 #dn; exec P1 #dn;
set #dn = 'buck'; exec P1 #dn; exec P1 #dn;
select * from names;
go
drop table names; drop procedure P1;
Output would 1, 1, 1, 2, 2 and the table content two rows big.