I created an SQL trigger in my Play database and it worked great. When I moved it over to Production, it suddenly won't work. We want the trigger to kick off whenever someone edits one of two custom fields in our database. The company who created the software already set up a trigger that kicks of any time a change is made to the database object (it just didn't track the changes made to custom fields). If I let my new trigger create a new record, I wound up with two audit records, so I changed my trigger to update the audit record the software company's trigger created. Could anyone tell me what I have done wrong? Here is my trigger:
USE [TmsEPrd]
GO
/****** Object: Trigger [dbo].[tr_Biograph_Udef_Audit_tracking] Script Date: 11/23/2020 10:22:57 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_Biograph_Udef_Audit_tracking] ON [dbo].[BIOGRAPH_MASTER] FOR UPDATE AS
BEGIN
IF EXISTS (SELECT 1 FROM deleted d
JOIN inserted i ON d.ID_NUM = i.ID_NUM
JOIN (SELECT ID_NUM, binary_checksum(UDEF_10A_1, UDEF_2A_4) AS inserted_checksum
FROM inserted) a ON i.ID_NUM = a.ID_NUM
JOIN (SELECT ID_NUM, binary_checksum(UDEF_10A_1, UDEF_2A_4) AS deleted_checksum
FROM deleted) b ON d.ID_NUM = b.ID_NUM
WHERE a.inserted_checksum <> b.deleted_checksum)
BEGIN
Update BIOGRAPH_HISTORY
set archive_job_name = 'UDEF_Change',
udef_2a_4 = i.udef_2a_4,
udef_2a_4_CHG = i.udef_2a_4_chg,
udef_10a_1 = i.udef_10a_1,
udef_10a_1_chg = i.udef_10a_1_chg
from
(select i.ID_NUM, SYSDATETIME()as job_time_a,
i.UDEF_10A_1, case when i.UDEF_10A_1 = d.UDEF_10A_1 then 0 when i.UDEF_10A_1 is null and d.UDEF_10A_1 is null then 0 else 1 end as UDEF_10A_1_CHG,
i.UDEF_2A_4, case when i.UDEF_2A_4 = d.UDEF_2A_4 then 0 when i.UDEF_2A_4 is null and d.UDEF_2A_4 is null then 0 else 1 end as UDEF_2A_4_CHG,
d.USER_NAME,d.JOB_NAME,d.JOB_TIME
FROM deleted d JOIN inserted i ON d.ID_NUM = i.ID_NUM) i
join BIOGRAPH_HISTORY b on i.ID_NUM = b.ID_NUM
where DATEDIFF(Minute, i.job_time_a, b.ARCHIVE_JOB_TIM) = 0
and b.ARCHIVE_JOB_NAME not like 'UDEF_Change%'
END;
END;
Try specifying #order = 'LAST' for your trigger. It might be that your trigger is executing first and not finding a record to update. In your test system, the trigger execution order might be reversed.
The order that triggers are created might affect trigger execution order, but this is not something to rely upon. When you think about it, this can be a headache. A test system that looks just like production can behave differently.
This is similar to relying upon a "natural" record order of a clustered index and not using a ORDER BY clause. A different execution plan can use a different index or go parallel resulting in a different or no order.
Related
I want to prevent the same #coupon_value in sp to submit and return
any message for validation using csharp but I am not able to how to
make changes in stored procedure.
CREATE PROCEDURE [dbo].[USP_REBATE_CAMPAIGN_RULE_DETAIL_VALIDATE]
#rebate_campaign_seq INT,
#coupon_value Varchar(50)='',
#Type varchar(50)='SERIES'
AS
SET NOCOUNT ON
BEGIN
SELECT rcrd.rebate_campaign_rule_detail_seq AS id,Type_value AS NAME,
'SERIES' AS type,
rcrd.amount_per_range AS Amount
FROM rebate_campaign_rule_detail rcrd (nolock)
INNER JOIN rebate_campaign_rule rcr
ON rcr.rebate_campaign_rule_seq = rcrd.rebate_campaign_rule_seq
INNER JOIN rebate_campaign rc
ON rc.rebate_campaign_seq = rcr.rebate_campaign_seq
WHERE rc.rebate_campaign_seq = #rebate_campaign_seq
AND rcrd.active_flag = 'Y' AND rcrd.type = #Type
AND rcrd.type_value=#coupon_value
End
The only way to prevent duplicate in database data is to add a UNIQUE CONSTRAINT. Everything else will fail, especially any solution coded with a procedural program, because of concurrency (imagine for a moment that two users launch the same procedure with the same values at the same time...).
To have a NULLbale UNIQUE constraint, you can add a UNIQUE filtered INDEX like this one :
CREATE INDEX X_UNIQUE_COUPON_RCRD
ON rebate_campaign_rule_detail (type_value)
WHERE type_value IS NOT NULL;
we're just migrating from mariadb (galera) to MSSQL.
One of our applications has a very special behaviour - from time to time (I have not found a pattern, the vendor uses very fancy AI-related stuff which noone can debug :-/) it will block the monitor-user of our loadbalancers because of too many connects, so the loadbalancer is no longer able to get the health state, suspends all services on all servers and the whole service is going down.
So I wrote a trigger which enables this user after he will be disabled.
I've already thought about a constraint which prohibits this, but then the application goes nuts if it will disable the user.
Anyway - in mysql this works perfectly for us:
delimiter $$
CREATE TRIGGER f5mon_no_disable AFTER UPDATE ON dpa_web_user
FOR EACH ROW
BEGIN
IF NEW.user_id = '99999999' AND NEW.enabled = 0 THEN
UPDATE dpa_web_user SET enabled = 1 WHERE user_id = '9999999';
END IF;
END$$
delimiter ;
I tried this in T-SQL (if it's important, it is MSSQL 2016)
CREATE TRIGGER f5mon_no_disable ON [dbo].[dpa_web_user]
AFTER UPDATE
AS
BEGIN
IF ( inserted.[user_id] = '9999999' AND inserted.[enabled] = 0 )
BEGIN
UPDATE dpa_web_user SET enabled = 1 WHERE user_id = '9999999';
END
END
I think it's the if-statement which is totally wrong in more than one way - but I do not have an idea how the syntax is in t-sql.
Thanks in advance for your help.
You can use IF EXISTS but you can't reference column values in inserted without set-based access to inserted:
IF EXISTS (SELECT 1 FROM inserted WHERE [user_id] = '9999999' AND [enabled] = 0)
BEGIN
UPDATE dpa_web_user SET enabled = 1 WHERE user_id = '9999999';
END
You may want to add AND enabled <> 1 to prevent updating a row for no reason.
You can do this in a single statement though:
UPDATE u
SET enabled = 1
FROM dbo.dpa_web_user AS u
INNER JOIN inserted AS i
ON u.[user_id] = i.[user_id]
WHERE u.[user_id] = '9999999'
AND i.[user_id] = '9999999'
AND u.enabled <> 1
AND i.enabled = 0;
I'm not a database expert but I am in need of some help making sure a trigger we are using to track an update on a table is the best way to handle our situation and is performing as it should. After loading the trigger we noticed some slow performance on the actual business system (user side).
Background: we are trying to capture the date/time of a transaction that happens so it can be referenced on a customer portal for our website.
The theory: the trigger monitors for a Update to a column to 'PI' and if that happens, it writes data to a table giving some basic information from 2 other tables that are related to to update.
Table 1 columns
RH.kbranch, RH.kordnum, RH.kcustnum, RH.custsnum, RH.[program]
Table 2 columns
RD.kbranch, RD.kordnum, RD.kpart
Table 3 columns (where trigger is attached)
EQ.kequipnum, EQ.eqpstatus
Trigger
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[PICKUPTrigger]
ON [TEST].[dbo].[equip]
FOR UPDATE
AS
IF (SELECT eqpstatus FROM inserted) = 'PI'
BEGIN
SET NOCOUNT ON
INSERT INTO [Workfiles].[dbo].[PickupAudit] ([HHBranch],[HHOrder],[HHCustomer], [HHShipTo], [EquipID], [EQStatus], [PickupNo], [StatusDate])
SELECT
RH.kbranch, RH.kordnum, RH.kcustnum, RH.custsnum,
RD.kpart, EQ.eqpstatus, RH.[program], GETDATE()
FROM
TEST.dbo.renthead RH
JOIN
TEST.dbo.rentdetl RD ON RH.kbranch = RD.kbranch
AND RH.kordnum = RD.kordnum
AND RH.program NOT LIKE 'OPSS%'
JOIN
TEST.dbo.equip EQ ON EQ.kequipnum = RD.kpart
WHERE
RD.kpart = (SELECT kequipnum FROM inserted);
END
The trigger works, but it appears to be causing problems and slowing down the actual user experience. Any help in tweaking what we have done is appreciated and if you have any questions, feel free to ask. Thanks.
You should use explicit joins:
INSERT INTO [Workfiles].[dbo].[PickupAudit]
([HHBranch],[HHOrder],[HHCustomer],[HHShipTo],[EquipID],[EQStatus],[PickupNo],[StatusDate])
SELECT RH.kbranch, RH.kordnum, RH.kcustnum, RH.custsnum, RD.kpart, EQ.eqpstatus, RH.[program], GETDATE()
FROM TEST.dbo.renthead RH JOIN
TEST.dbo.rentdetl RD
ON RH.kbranch = RD.kbranch AND
RH.kordnum = RD.kordnum AND
RH.program NOT LIKE 'OPSS%' JOIN
TEST.dbo.equip EQ
ON EQ.kequipnum = RD.kpart JOIN
inserted i
ON RD.kpart = i.kequipnum;
For performance, you want indexes on the columns used in the JOINs, in this order:
TEST.dbo.rentdetl(kpart, kbanch, kordnum)
TEST.dbo.equip(kequipnum)
TEST.dbo.renthead(kbranch, kbanch, program)
The slow is caused by th join statements, as I think you join heavy tables with data or tables under load sometimes at the trigger operation time
The solution to get better performance is to create "indexed view" not just "view"
And use it in trigger, and you will see drastically affect
I've got problem with INSTEAD OF trigger in MsSQL. I got an app with some error, and as a quick workaround I don't want user to modify one exact row in DB.
I created following trigger: (on table)
create trigger UglyWorkaround ON Configuration instead of update
as
begin
if (select COUNT(1) from inserted where Key='Key01' and Value<>'2') > 0 begin
update Configuration set Value='2' where Key='Key01'
end else begin
-- DEFAULT ACTION (do update as intended)
end;
end;
But I've got problem with determining, how to set default action.
Update Configuration set Value=inserted.Value where Key=inserted.Key doesn't work for me. Is there any way how to do this with triggers? (I know that the solution is bad, but I got no other option, as I can't change code now.)
inserted is a table, so try joining:
update c set c.Value = i.Value
from Configuration c
inner join inserted i on c.Key = i.Key
You could also filter out Key01 at the same time, and it wouldn't matter if they tried to update the value for Key01 to something other than 2.
update c set c.Value = i.Value
from Configuration c
inner join inserted i on c.Key = i.Key
where i.Key <> 'Key01'
The INSTEAD OF part means that you're going to have to update the other columns as well as the value one.
There's no need to do this with an if .. else as the logic can be done at column level
create trigger NicerWorkaround ON Configuration instead of update
as
begin
update c set
c.Value = CASE WHEN i.Key='Key01' THEN '2' ELSE i.Value END,
c.OtherColumn1 = i.OtherColumn1,
c.OtherColumn2 = i.OtherColumn2
from Configuration c
inner join inserted i on c.Key = i.Key
END;
If you want to prevent the edit perform a rollback on the change, why are you setting Value='2' when you find a matching row?
You don't have to supply a default action, doing nothing in the trigger will let the update happen.
The command:
select * from dbo.hmg_cahplots
returns 9250 rows. However, when I try to create a trigger, it fails with:
Msg 8197, Level 16, State 6, Procedure LotUpdateTrigger_tdp, Line 1
The object 'dbo.hmg_cahplots' does not exist or is invalid for this
operation.
Trigger code is:
CREATE TRIGGER dbo.LotUpdateTrigger_tdp ON dbo.hmg_cahplots FOR UPDATE, INSERT
AS
BEGIN
update lot
set lot.hmg_planmodelname = model.hmg_modelname, lot.hmg_thermslotincentive = model.hmg_thermsincentive,
lot.hmg_thermslotincentive_base = model.hmg_thermsincentive_base, lot.hmg_kwlotincentive = model.hmg_kwincentive
from hmg_cahplots as lot inner join i
on lot.hmg_cahplotsid = i.hmg_cahplotsid
inner join hmg_pgecahp as proj
on proj.hmg_pgecahpid = lot.hmg_pgecahplots
left outer join hmg_pgecahpmodels as model
on model.hmg_pgecahpmodelsid = lot.hmg_cahpplanstolotsid
and model.hmg_pgecahpplansid = lot.hmg_pgecahplots
END
I doubt this is very hard to solve. I assume I need to specify a namespace or something. However, I'm new to SQL Server and I don't have any idea how to start on this.
Thanks -- Tim
Are you sure you are located in correct database, not master?
Are you sure your permissions are fine?
Are you sure this is a table, not a view?
If you are sure that this table exists and you are trying to create trigger in the same database, then remove coma just before from and after lot.hmg_kwlotincentive = model.hmg_kwincentive,.
Two problems:
Triggers are mostly Schema-locked.
You are using more than one.
use the same schema and ADD:
-- AT start add the code
USE [DATABASE] --switch with database name
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--At the END add
END
GO