Unable to update rows individually - triggers

I am trying to update our patient table within our system, but due to a connection we have with the state that sends modifications to that table to an external system I'm unable to do mass updates due to several triggers in our system (Microsoft SQL Server 2008 R2). The main piece of code in the trigger that is preventing
IF (#numrows > 1)
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION
SELECT #errmsg = OBJECT_NAME(##PROCID) + ' : more than one row is updated in table Patient'
RAISERROR(#errmsg,16,21)
RETURN
END
I cannot simply shut these triggers off where it would break a lot of things, so to do what would be a simple act of
update patient set security_level = '2'
where security_level = '1'
I used the following code which used to work in prior versions
declare #tmp_table table(
PRG int identity(1,1) Primary Key,
patient_id int
)
declare #start_value int = 1,
#finish_value int,
#patient_id int
Insert Into #tmp_table(patient_id ) Select patient_id From patient where security_level = '1'
Select #finish_value = max(PRG) From #tmp_table
While #start_value <= #finish_value
Begin
--now get a key for patient and store in variables
Select #patient_id = patient_id
From #tmp_table
Where PRG = #start_value
--.. and now update by key
Update patient
set security_level = '2'
Where patient_id = #patient_id
Set #start_value = #start_value + 1
End
I get the following error upon running that code
Msg 50000, Level 16, State 21, Procedure tU_Patient_HL7, Line 64
tU_Patient_HL7 : more than one row is updated in table Patient
Msg 3609, Level 16, State 1, Line 22
The transaction ended in the trigger. The batch has been aborted.
Any idea how I could tweak this or recode this to update the security level of all patients who have a security level set at 1 and switch it to 2?
Update
Would there be any way I could loop
Update top (1) patient
set security_level = '2'
where security_level = '1'
Until all rows are affected? That would work as well.

With out full code ,it is hard .I am guessing your update statement is conflicting with below peice of code..
IF (#numrows > 1)
even though you use
Update patient
set security_level = '2'
Where patient_id = #patient_id
Your update query may affect more than one row.So my best bet would be to tweak your update query to below or change trigger if you can which is desirable
Update top (1) patient
set security_level = '2'
Where patient_id = #patient_id

If you add to your update code a "GO 100", that should do the trick. You may need to put a delay in there as well if it updates too fast for the trigger.
Update top (1) patient
set security_level = '2'
where security_level = '1'
GO 100
The number of executions (i.e. 100 in my example) is up to you.

Related

DB2 measure execution time of triggers

How can I best measure the execution time of triggers in DB2 for insert or update.
It is needed for some performance issues, some of them are behaving very slow.
CREATE OR REPLACE TRIGGER CHECK
NO CASCADE BEFORE INSERT ON DAG
REFERENCING NEW AS OBJ
FOR EACH ROW MODE DB2SQL
WHEN(
xyz)
)
SIGNAL SQLSTATE xxx)
For compiled triggers (that is, with BEGIN ... END body):
SELECT
T.TRIGNAME
, M.SECTION_NUMBER
, M.STMT_EXEC_TIME
, M.NUM_EXEC_WITH_METRICS
-- Other M metrics
, M.*
, M.STMT_TEXT
FROM SYSCAT.TRIGGERS T
JOIN SYSCAT.TRIGDEP D
ON (D.TRIGSCHEMA, D.TRIGNAME) = (T.TRIGSCHEMA, T.TRIGNAME)
JOIN TABLE (MON_GET_PKG_CACHE_STMT (NULL, NULL, NULL, -2)) M
ON (M.PACKAGE_SCHEMA, M.PACKAGE_NAME) = (D.BSCHEMA, D.BNAME)
WHERE D.BTYPE = 'K'
-- Or use whatever filter on your triggers
AND (T.TABSCHEMA, T.TABNAME) = ('MYSCHEMA', 'MYTABLE')
ORDER BY 1, 2
For inlined triggers (that is, with BEGIN ATOMIC ... END body):
No way to get separate statistics for them. They are compiled and optimized with the corresponding statement fired them.

I want to add database triggers to record changes to the RESB table in SAP HANA to enable delta extraction

We are running ECC6 on HANA, currently the extraction of RESB does a full table scan. I want to record that a change has happened to a Z table (I don't care what change).
I've attempted to create the triggers (in Word) from Help & Google, I would appreciate any suggestions for improvement.
create trigger tr_RESB_insert
after insert on RESB
referencing new row NR
for each row
begin
insert into ZBW_RESB_CDC (
MANDT
,RSNUM
,RSPOS
,RSART
,CHNGIND
,AEDAT
,AEZEIT
,AETMSTMP
,TZONE
,DELFLG )
values ( NR.MANDT
, NR.RSNUM
, NR.RSPOS
, NR.RSART
, ‘I’
, current_utcdate
, current_utctime
, current_utctimestamp
, ‘UTC’
,’ ’
)
end;
and
create trigger tr_RESB_update
after update on RESB
referencing new row NR
for each row
begin
merge ZBW_RESB_CDC as T
using NR
on ( NR.MANDT = T.MANDT and
,NR.RSNUM = T.RSNUM and
,NR.RSPOS = T.RSPOS and
,NR.RSART = T.RSART )
when matched then update
set T.CHNGIND = ‘U’
,T.AEDAT = current_utcdate
,T.AEZEIT = current_utctime
,T.AETMSTMP = current_utctimestamp
,T.TZONE = ‘UTC’
,T.DELFLG = ‘ ’
when not matched then insert (
MANDT
,RSNUM
,RSPOS
,RSART
,CHNGIND
,AEDAT
,AEZEIT
,AETMSTMP
,TZONE
,DELFLG )
values ( NR.MANDT
,NR.RSNUM
,NR.RSPOS
,NR.RSART
, ‘U’
, current_utcdate
, current_utctime
, current_utctimestamp
, ‘UTC’
,’ ’
end;
similarly for delete
At some time we'll upgrade to S4 but for the moment I would like to improve what we have.
I chose to use MERGE so that I don't need to initialise the table and so I can delete old records. Nor do I want to keep images of every change, just the fact that it has changed.
Thanks,
G.

Merge join not able to join properly on varchar column

I've created below code to implement SCD type 2 using merge, when i run the code i'm getting primary key violations on csname field. I have the below values as part of primary key, not sure whether merge SQL does support for varchar or not.
if I run the normal inner join SQL on the same key then i'm getting the matching records as well.
Any help much appreciated
csname
ER - Building Complaints
TR - Building Applications
CREATE PROCEDURE dbo.load_target
AS
BEGIN
INSERT INTO [TR_DW].[enum].[Rt]([csname],[enddatetime],[EffectiveToDate],[EffectiveFromDate],[CurrentRecord])
SELECT[csname],[enddatetime],[EffectiveToDate],[EffectiveFromDate],[CurrentRecord]
FROM
(
MERGE [TR_DW].[enum].[Rt] RtCSQSuTT
USING [TR].[enum].[Rt] RtCSQSuST
ON (RtCSQSuTT.csname = RtCSQSuST.csname)
WHEN NOT MATCHED THEN
INSERT ([csname],[enddatetime],[EffectiveToDate],[EffectiveFromDate],[CurrentRecord])
VALUES ([csname],[enddatetime],'12/31/9999', getdate(), 'Y')
WHEN MATCHED AND RtCSQSuTT.[CurrentRecord] = 'Y' AND
(ISNULL(RtCSQSuTT.[enddatetime], '') != ISNULL(RtCSQSuST.[enddatetime], ''))THEN
UPDATE SET
RtCSQSuTT.[CurrentRecord] = 'N',
RtCSQSuTT.[EffectiveFromDate] = GETDATE() - 1,
RtCSQSuTT.[EffectiveToDate] = GETDATE()
OUTPUT $Action Action_Taken,RtCSQSuST.[csqname],RtCSQSuST.[enddatetime],'12/31/9999' AS[EffectiveToDate],GETDATE() AS[EffectiveFromDate],'Y' AS[CurrentRecord]
)AS MERGE_OUT21
WHERE MERGE_OUT21.Action_Taken = 'UPDATE';
END
GO

Cannot insert NULL value into column error

I have a issue where I want to update a column in a table and with a trigger to update same column but in another table. It says I cannot insert NULL but I can't seem to understand from where it gets that NULL value. This is the trigger:
CREATE TRIGGER Custom_WF_Update_WF_DefinitionSteps_DefinitionId ON WF.Definition
AFTER UPDATE AS BEGIN
IF UPDATE(DefinitionId)
IF TRIGGER_NESTLEVEL() < 2
BEGIN
ALTER TABLE WF.DefinitionSteps NOCHECK CONSTRAINT ALL
UPDATE WF.DefinitionSteps
SET DefinitionId =
(SELECT i.DefinitionId
FROM inserted i,
deleted d
WHERE WF.DefinitionSteps.DefinitionId = d.DefinitionId
AND i.oldPkCol = d.DefinitionId)
WHERE WF.DefinitionSteps.DefinitionId IN
(SELECT DefinitionId FROM deleted)
ALTER TABLE WF.DefinitionSteps CHECK CONSTRAINT ALL
END
END
This update statement works just fine:
UPDATE [CCHMergeIntermediate].[WF].[Definition]
SET DefinitionId = source.DefinitionId + 445
FROM [CCHMergeIntermediate].[WF].[Definition] source
But this one fails:
UPDATE [CCHMergeIntermediate].[WF].[Definition]
SET DefinitionId = target.DefinitionId
FROM [CCHMergeIntermediate].[WF].[Definition] source
INNER JOIN [centralq3].[WF].[Definition] target
ON (((source.Name = target.Name) OR (source.Name IS NULL AND target.Name IS NULL)))
I get the following error:
Msg 515, Level 16, State 2, Procedure Custom_WF_Update_WF_DefinitionSteps_DefinitionId, Line 7
Cannot insert the value NULL into column 'DefinitionId', table 'CCHMergeIntermediate.WF.DefinitionSteps'; column does not allow nulls. UPDATE fails.
If I do a select instead of the update statement, like this:
SELECT source.DefinitionId, target.DefinitionId
FROM [CCHMergeIntermediate].[WF].[Definition] source
INNER JOIN [centralq3].[WF].[Definition] target
ON (((source.Name = target.Name) OR (source.Name IS NULL AND target.Name IS NULL)))
I get this result:
http://i.stack.imgur.com/3cZsM.png (sorry for external link, I don't have enaugh reputation to post image here )
What am I doing wrong? What I don't see? What am I missing..?
The problem was in the trigger at the condition. I modified the second where from i.oldPkCol = d.DefinitionId to i.oldPkCol = **d.oldPkCol** and it worked.
UPDATE WF.DefinitionSteps
SET DefinitionId =
(SELECT i.DefinitionId
FROM inserted i,
deleted d
WHERE WF.DefinitionSteps.DefinitionId = d.DefinitionId
AND i.oldPkCol = **d.oldPkCol**)
WHERE WF.DefinitionSteps.DefinitionId IN
(SELECT DefinitionId FROM deleted)

SQL SErver Trigger not evaluating as Insert or Update properly

I want to have one trigger to handle updates and inserts. Most of the sql actions in the trigger are for both. The only exception is the fields I'm using to record date and username for an insert and an update. This is what I have, but the updates of the fields used to track update and insert are not firing right. If I insert a new record, I get CreatedBy, CreatedOn, LastEditedBy, LastEditedOn populated, with LastEditedOn as 1 second after CreatedOn (which I dont want to happen). When I update the record, only the LastEditedBy & LastEditedOn changes (which is correct). I'm including my full trigger for reference:
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
-- =================================================================================
-- Author: Paul J. Scipione
-- Create date: 2/15/2012
-- Update date: 6/5/2012
-- Description: To concatenate several fields into a set formatted UnitDescription,
-- to total Span & Loop footages, to set appropriate AcctCode, & track
-- user inserts
-- =================================================================================
IF OBJECT_ID('ProcessCable', 'TR') IS NOT NULL
DROP TRIGGER ProcessCable
GO
CREATE TRIGGER ProcessCable
ON Cable
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- IF TRIGGER_NESTLEVEL() > 1 RETURN
IF ((SELECT TRIGGER_NESTLEVEL()) > 1 )
RETURN
ELSE
BEGIN
-- record user and date of insert or update
IF EXISTS (SELECT * FROM DELETED)
UPDATE Cable SET LastEditedOn = getdate(), LastEditedBy = REPLACE(user_name(), 'GRTINET\', '')
ELSE IF NOT EXISTS (SELECT * FROM DELETED)
UPDATE Cable SET CreatedOn = getdate(), CreatedBy = REPLACE(user_name(), 'GRTINET\', '')
-- reset Suffix if applicable
UPDATE Cable SET Suffix = NULL WHERE Suffix = 'n/a'
-- create UnitDescription value
UPDATE Cable SET UnitDescription =
isnull (Type, '') +
isnull (CONVERT (NVARCHAR (10), Size), '') +
'-' +
isnull (CONVERT (NVARCHAR (10), Gauge), '') +
CASE
WHEN ExtraTrench IS NOT NULL AND ExtraTrench > 0 THEN
CASE
WHEN Suffix IS NULL THEN 'TE' + '(' + CONVERT (NVARCHAR (10), ExtraTrench) + ')'
ELSE 'TE' + '(' + CONVERT (NVARCHAR (10), ExtraTrench) + ')' + Suffix
END
ELSE isnull (Suffix, '')
END
-- convert any accidental negative numbers entered
UPDATE Cable SET Length = ABS(Length)
-- sum Length with LoopFootage into TotalFootage
UPDATE Cable SET TotalFootage = isnull(Length, 0) + isnull(LoopFootage, 0)
-- set proper AcctCode based on Type
UPDATE Cable SET AcctCode =
CASE
WHEN Type IN ('SEA', 'CW', 'CJ') THEN '32.2421.2'
WHEN Type IN ('BFC', 'BJ', 'SEB') THEN '32.2423.2'
WHEN Type IN ('TIP','UF') THEN '32.2422.2'
WHEN Type = 'unknown' OR Type IS NULL THEN 'unknown'
END
WHERE AcctCode IS NULL OR AcctCode = ' '
END
END
GO
A few things jump out at me when I look at your trigger:
You are doing several additional updates rather than a single update (performance-wise, a single update would be better).
Your update statements are unconstrained (there is no JOIN to the inserted/deleted tables to limit the number of records that you perform these additional updates on).
Most of this logic feels like it should be in the application layer rather than in the database; Or, perhaps in some cases implemented differently.
Some quick examples:
Suffix of "n/a" should be removed before inserted.
Cable length absolute value should be done before inserted (with a CHECK CONSTRAINT to verify that bad data cannot be inserted).
TotalFootage should be a computed column so it is always correct.
The Type/AcctCode relationship seems like it should be a column value in a foreign key reference.
But ultimately, I think the reason you are seeing the unexpected dates is because of the unconstrained updates. Without addressing any of the other concerns I brought up above, the statement that sets the audit fields should be more like this:
UPDATE Cable SET LastEditedOn = getdate(), LastEditedBy = REPLACE(user_name(), 'GRTINET\', '')
FROM Cable
JOIN deleted on Cable.PrimaryKeyColumn = deleted.PrimaryKeyColumn
UPDATE Cable SET CreatedOn = getdate(), CreatedBy = REPLACE(user_name(), 'GRTINET\', '')
FROM Cable
JOIN inserted on Cable.PrimaryKeyColumn = inserted.PrimaryKeyColumn
LEFT JOIN deleted on Cable.PrimaryKeyColumn = deleted.PrimaryKeyColumn
WHERE deleted.PrimaryKeyColumn IS NULL