Default action of INSTEAD OF UPDATE trigger on MsSQL DB - sql-server

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.

Related

prevent duplicate value to submit using stored procedure in sql server

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;

MSSQL - IF within a TRIGGER

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;

SQL Trigger Works in Play but not Production

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.

checking if the same row exists in the table or not [duplicate]

I've got a table with data named energydata
it has just three columns
(webmeterID, DateTime, kWh)
I have a new set of updated data in a table temp_energydata.
The DateTime and the webmeterID stay the same. But the kWh values need updating from temp_energydata table.
How do I write the T-SQL for this the correct way?
Assuming you want an actual SQL Server MERGE statement:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh);
If you also want to delete records in the target that aren't in the source:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Because this has become a bit more popular, I feel like I should expand this answer a bit with some caveats to be aware of.
First, there are several blogs which report concurrency issues with the MERGE statement in older versions of SQL Server. I do not know if this issue has ever been addressed in later editions. Either way, this can largely be worked around by specifying the HOLDLOCK or SERIALIZABLE lock hint:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
[...]
You can also accomplish the same thing with more restrictive transaction isolation levels.
There are several other known issues with MERGE. (Note that since Microsoft nuked Connect and didn't link issues in the old system to issues in the new system, these older issues are hard to track down. Thanks, Microsoft!) From what I can tell, most of them are not common problems or can be worked around with the same locking hints as above, but I haven't tested them.
As it is, even though I've never had any problems with the MERGE statement myself, I always use the WITH (HOLDLOCK) hint now, and I prefer to use the statement only in the most straightforward of cases.
I often used Bacon Bits great answer as I just can not memorize the syntax.
But I usually add a CTE as an addition to make the DELETE part more useful because very often you will want to apply the merge only to a part of the target table.
WITH target as (
SELECT * FROM dbo.energydate WHERE DateTime > GETDATE()
)
MERGE INTO target WITH (HOLDLOCK)
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
If you need just update your records in energydata based on data in temp_energydata, assuming that temp_enerydata doesn't contain any new records, then try this:
UPDATE e SET e.kWh = t.kWh
FROM energydata e INNER JOIN
temp_energydata t ON e.webmeterID = t.webmeterID AND
e.DateTime = t.DateTime
Here is working sqlfiddle
But if temp_energydata contains new records and you need to insert it to energydata preferably with one statement then you should definitely go with the answer that Bacon Bits gave.
UPDATE ed
SET ed.kWh = ted.kWh
FROM energydata ed
INNER JOIN temp_energydata ted ON ted.webmeterID = ed.webmeterID
Update energydata set energydata.kWh = temp.kWh
where energydata.webmeterID = (select webmeterID from temp_energydata as temp)
THE CORRECT WAY IS :
UPDATE test1
INNER JOIN test2 ON (test1.id = test2.id)
SET test1.data = test2.data

SQL Server won't create trigger on table I can query

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

Resources