Incrementing RECORD_ID When insterting records using a trigger - sql-server

I am using a trigger to insert rows into a table using INSERT statement as below but when doing this the RECORD_ID number increments by 1 digit so all the records inserted have the same number..
This is what i'm using to increment the records from the trigger.
, ISNULL((
SELECT MAX([PROGRESS-RECID]) FROM [DBAdmin].[dbo].[ReTncyTransStatement]
),0) + 1 AS [PROGRESS-RECID]
This is what i'm using to load the data
;WITH TestTrans (
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE]
)
AS (SELECT
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE] from [SQLViewsPro2Live].[dbo].[RE-TNCY-TRANS] where [TRANS-DATE] between '2019-05-16 00:00:00.000' and '2019-05-17 00:00:00.000'
)
INSERT INTO [SQLViewsPro2Test].[dbo].[RE-TNCY-TRANS]
SELECT
[ORG-CODE]
,[TNCY-SYS-REF]
,[TRANS-NO]
,[POSTING-YEAR]
,[POSTING-WEEK]
,[TRANS-YEAR]
,[TRANS-WEEK]
,[TRANS-DATE]
,[ACCOUNT-TYPE]
,[ACCOUNT-CODE]
,[COMMENT]
,[TRANS-AMT]
,[SOURCE]
,[CREATED-USER]
,[CREATED-DATE]
,[CREATED-TIME]
,[UPDATED-USER]
,[UPDATED-DATE]
,[UPDATED-TIME]
,[BATCH-NO]
,[BATCH-NO-TYPE]
,[SUSPENSE-REF]
,[REFERENCE]
,[MGT-AREA]
,[ANALYSIS-CODE]
FROM TestTrans;
GO
Any fixes appreciated
Thanks,
Full description of problem available here: T-SQL : create trigger to copy new columns from one table to another and increment no

Make PROGRESS-RECID an IDENTITY column and it will auto-increment.

Based on the linked question, you can rewrite your trigger as following:
CREATE TRIGGER AddReTncyTransStatement
ON [SQLViewsPro2EOD].[dbo].[RE-TNCY-TRANS]
AFTER UPDATE, INSERT
AS
BEGIN
DECLARE #ORG_CODE INT,
#TNCY_SYS_REF INT,
#TRANS_NO INT;
DECLARE C CURSOR FAST_FORWARD FOR(
SELECT Inserted.[ORG-CODE],
Inserted.[TNCY-SYS-REF],
Inserted.[TRANS-NO]
FROM Inserted);
OPEN C;
FETCH NEXT FROM C
INTO #ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO;
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [DBAdmin].[dbo].[ReTncyTransStatement]
(
[ORG-CODE],
[TNCY-SYS-REF],
[TRANS-NO],
[PROGRESS-RECID]
)
SELECT
#ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO,
ISNULL((SELECT MAX([PROGRESS-RECID]) FROM [DBAdmin].[dbo].[ReTncyTransStatement]),0) + 1 AS RECID;
FETCH NEXT FROM C
INTO #ORG_CODE,
#TNCY_SYS_REF,
#TRANS_NO
END;
CLOSE C;
DEALLOCATE C;
END;
Root of your problem:
When you use INSERT INTO ... SELECT(The one outside the trigger), trigger will be called once and the inserted table will contain all the records to be inserted. so the query inside the trigger will be run once, furthermore the SELECT MAX([PROGRESS-RECID]) will be calculated once. This means that if the inserted table contains 10 records, that are being inserted, then MAX(...) will be same for all of them!
How I Solved it:
Inside the trigger I used Cursor to iterate through the all records that are being inserted(For example 10 records), then in each iteration I insert one record to ReTncyTransStatement so the MAX(...) will be calculated and executed as expected.

Related

trying to do multiple inserts in one trigger using field dependent on first insert

I am trying to manipulate a bunch of table triggers that start with an insert into one event table (TB A). This insert fires a trigger (T1) that does an insert into a secondary table (TB B). The secondary table has an insert trigger (T2) that does an update on the first table (TB A).
Pardon the confusion but basically I wanted to ensure that for the first trigger, do a second insert in the same table using the values of the first insert.
BEGIN
SET NOCOUNT ON
declare #Time int
declare #DeleteLinger int
select #Time = convert(integer,value) from Systemproperty
where [name] = 'KeepStoreLingerTimeInMinutes'
select #DeleteLinger = convert(integer,value) from Systemproperty
where [name] = 'KeepDeleteLingerTimeInMinutes'
IF (#DeleteLinger >= #Time) SET #Time=#DeleteLinger+1
insert StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,GFlags)
select DateAdd(mi,#Time,getutcdate()), 1, I.ID, r.ID, r.GFlags
from inserted I, StorageRule r
where r.Active=1 and I.Active=0 and (I.OnlineCount > 0 OR
I.OnlineScreenCount > 0)
-- try and get the value that was just inserted into StorageQueue
select #SFlags=S.GFlags FROM StorageQueue S, StorageRule r, inserted I
WHERE r.ID = S.RuleID and I.ID = S.parameter
-- if a certain value do another insert into StorageQueue
If (#SFlags = 10)
INSERT INTO StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,StoreFlags)
VALUES(DateAdd(mi,#Time,getutcdate()), 1, (SELECT parameter
FROM StorageQueue),2, #SFlags)
END
The problem is that there seems to be either an issue that the record is not yet inserted because the variable #SFlags is null or some other trigger accesses the values and makes changes. My question is whether this is a good way to do it. Is it possible to retrieve a value into the variable from within a trigger because it seems whichever way I try it, it doesnt work.

SQL SERVER Select multiple fields from If Exists

I have to do an SQL Server Statement that have to return an empty row when is null, and data otherwhise.
I am trying to do a Select from (if exisits) but have an error on parent table.
I Simplify it. But the meaning, is to retrieve a couple of fields when condition is null and other fields when it is not null.
It Works fine when I do not clouse it in another select.... I need to retrieve it as a table to do an inner Join with other clouse.
How can i resolved it?
Here is my code..
select * from
(
if exists(select isnull(SECTOR_ID_DESTINO_BAD,-1)
from workflow_Compras_detalle w
where w.id=2)
begin
select null as Sector,null as sector_id_origen
end
else
begin
select top 1 isnull(ws.sector,'') sector, wd.sector_id_origen
from workflow_Compras_detalle wd
where orden < 10
end
)Table
you should try to insert the data into a temporary table or Table Variable, then get the data from that table, here is an example with a Table Variable, if you need something more persistent you may use a #Temp Table, i recommend you take a look to this: difference between var table and #Temp Table
DECLARE #VAR_TABLE AS TABLE(
Sector varchar(25),
sector_id_origen int
)
if exists(select isnull(SECTOR_ID_DESTINO_BAD,-1)
from workflow_Compras_detalle w
where w.id=2)
begin
INSERT INTO #VAR_TABLE
Select null as Sector,null as sector_id_origen
End
Else
begin
INSERT INTO #VAR_TABLE
select top 1 isnull(ws.sector,'') sector, wd.sector_id_origen
from workflow_Compras_detalle wd
where orden < 10
End
SELECT * FROM #VAR_TABLE

SQL Server: how to re-use SCOPE_IDENTITY()?

I want to do some sort of "copy-paste" of one row in the table creneau. This row has a one-to-many relationship with table creneau_jour. Thus I'd like to duplicate all the rows of this relationship too. I'm trying to do what I've done with MySQL but I'm missing something but dont know why:
INSERT INTO [RdV].[dbo].[creneau]
([libelle]
,[debut]
,[fin]
,[heure_debut]
,[duree])
SELECT
'Encore !!!', debut, fin, heure_debut, duree
FROM
creneau
WHERE
id = 1;
INSERT INTO [RdV].[dbo].[creneau_jour]
([creneau_id] ,[jour_semaine])
VALUES (
SELECT SCOPE_IDENTITY(), SELECT jour_semaine FROM creneau_jour WHERE id=1
);
Any idea of what I'm doing wrong?
DECLARE #SCOPEIDENTITY INT;
INSERT INTO [RdV].[dbo].[creneau]
([libelle]
,[debut]
,[fin]
,[heure_debut]
,[duree])
SELECT 'Encore v3',debut,fin,heure_debut,duree FROM creneau WHERE id=1;
SET #SCOPEIDENTITY = SCOPE_IDENTITY();
INSERT INTO [RdV].[dbo].[creneau_jour] ([creneau_id] ,[jour_semaine])
SELECT #SCOPEIDENTITY, jour_semaine FROM creneau_jour WHERE creneau_id=1
;
GO
You can forget about using SCOPE_IDENTITY() and use OUTPUT as part of the INSERT, so that you can set a variable with a value from the newly created row.
One such advantage to this method is that if you ever turn on identity insert then #SCOPE_IDENTITY wouldn't work, but the method using OUTPUT would.
DECLARE #Identity INT
INSERT INTO [RdV].[dbo].[creneau]
([libelle]
,[debut]
,[fin]
,[heure_debut]
,[duree])
OUTPUT inserted.IdentityColumn INTO #Identity
SELECT
'Encore !!!', debut, fin, heure_debut, duree
FROM
creneau
WHERE
id = 1
INSERT INTO [RdV].[dbo].[creneau_jour]
([creneau_id] ,[jour_semaine])
VALUES (
SELECT #Identity, SELECT jour_semaine FROM creneau_jour WHERE id=1
);
Try this one
DECLARE #SCOPEIDENTITY INT;
INSERT INTO [RdV].[dbo].[creneau]
([libelle]
,[debut]
,[fin]
,[heure_debut]
,[duree])
SELECT 'Encore v3',debut,fin,heure_debut,duree FROM creneau WHERE id=1;
SET #SCOPEIDENTITY = SCOPE_IDENTITY();
INSERT INTO [RdV].[dbo].[creneau_jour] ([creneau_id] ,[jour_semaine])
SELECT #SCOPEIDENTITY, jour_semaine FROM creneau_jour WHERE creneau_id=1;
GO

Slow insert with "While Exists" loop

I'm trying to insert a lot of records to a table.
This is the scenario:
SQL Server 2008 (DB is 2005)
The destination table has a Clustered Index (PK). This field should be an Identity, but the developer of the DB (we couldn't change it, as it will affect the program) create it as an Integer. Everytime the program needs to add a row to the table, look at the max id (historyno on this case) and sum one.
This affect our performance when we need to insert a lot of records at the same time, so we create a process to insert rows from a temporary table (AKT_ES_CampTool_TempHist) out of production hours.
The problem is that, in one hour, it only inserts 8K rows. Considering that we need to insert more than 120K, we run out of hours.
The code we use is the following. Please, if someone has any idea to improve it, it will be appreciate.
DECLARE #HistNo AS INT
WHILE EXISTS (SELECT * FROM AKT_ES_CampTool_TempHist WHERE Inserted = 0)
BEGIN
SELECT #HistNo=MIN(HistoryNo) FROM AKT_ES_CampTool_TempHist WHERE Inserted = 0
INSERT INTO NOVADB.dbo.niHist (
HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity
)
SELECT
(SELECT max(historyNo)+1
FROM NOVADB..niHist),ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity
FROM AKT_ES_CampTool_TempHist
WHERE HistoryNo=#HistNo
UPDATE AKT_ES_CampTool_TempHist
SET Inserted=1
WHERE HistoryNo=#HistNo
END
obviously the proper answer is to change that historyNo column to an identity, but as you can't do that why not use ROW_NUMBER over the entire set to get an incrementing number to add to the prev max historyNo?
Then you could alter the insert to just
DECLARE #OldMaxHistNo AS INT
SELECT #OldMaxHistNo = MAX(historyNo) FROM NOVADB..niHist
INSERT INTO NOVADB.dbo.niHist (
HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity
)
SELECT
#OldMaxHistNo+ ROW_NUMBER() OVER(ORDER BY ObjectNo)
FROM NOVADB..niHist),ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity
FROM AKT_ES_CampTool_TempHist
WHERE Inserted = 0
UPDATE AKT_ES_CampTool_TempHist
SET Inserted=1
Might have to lock the tables inside a transaction whilst doing it though
You could select the data which should be inserted into an temporary table with a new HistoryNo generated by Rownumber() and changed with max(historyNo) FROM NOVADB..niHist.
SELECT ROW_NUMBER() OVER (Order by ID) as NEW_HistoryNo , *
into #tmp
FROM AKT_ES_CampTool_TempHist
WHERE Inserted = 0
ORDER BY HistoryNo
Update #tmp set NEW_HistoryNo=NEW_HistoryNo + (SELECT max(historyNo) FROM NOVADB..niHist)
INSERT INTO NOVADB.dbo.niHist (
HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity ) )
SELECT
NEW_HistoryNo,ObjectType,ObjectNo,SubNo,ReferenceNo,
Time,Type,Priority,Collector,Code,
Action,RemainingAmount,Obliterated,SubType,ActSegment,
Data,FreetextData,quantity
from #tmp
Update AKT_ES_CampTool_TempHist set Inserted = 1
from #tmp
Where #tmp.HistoryNo=AKT_ES_CampTool_TempHist.HistoryNo and AKT_ES_CampTool_TempHist.Inserted = 0
Drop Table #tmp
You should never use the max+1 strategy you are using for assigning an index. Assuming you can't use identity and the main table and you are not using the lastest version of sql server -- Create a shadow table based on a identity field and use that to generate sequence numbers
i.e.
create table AKT_ES_CampTool_Shadow
(
id int identity(1234,1) not null -- replacing 1234 with a value based on max+1
, dummy varchar(1) null
)
Then to gen an id -- less expensive than max+1 -- no locking problems
create proc AKT_ES_CampTool_idgen(#newid output)
(
declare #newid int
begin tran
insert into dbo.AKT_ES_CampTool_Shadow (dummy) values ('')
select #newid = scope_id()
rollback
)
You don't say how big AKT_ES_CampTool_TempHist is. If it is large, you may have performance issues there (esp. if there is no index on the field "inserted")
You could start by created a table var containing the relevant columns.
declare #TempHist table
(
HistNo int
, inserted int
, etc.
primary key(...)
)
Then populate #TempHist with a single insert query. If you don't have an appropriate PK for this table, used use a generated RowID s the PK
Now, you can loop through this table without causing lock contention. Just select top 1 from #TempHist and the delete the corresponsding row from #TempHist when you are done processing it.
You won't have use a cursor nor have a large Batch operation

Unable to set value based on INSERTED table in trigger

GOAL:I'm creating after insert trigger that should insert new record to OrderSuspendRule table based on rule in this table that was related with Promotion of which new version was created.
PROBLEM
I cannot set value to #SUS_ID. Select returns value but it isn't set to variable.
Sample insert:
INSERT INTO PromotionHeader (Guid,CreatedAt,UpdatedAt,IsActive,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version],StatusId,PreviousId)
select newid(),CreatedAt,UpdatedAt,1,CompanyId,UpdatedById,CreatedById,Name,[Description],ValidFrom,ValidTo,BusinessUnitId,OfferId,[Version]+1,StatusId,916 FROM PromotionHeader WHERE Id=916
Where PreviousId points to older version of promotion.
CREATE TRIGGER TRIG1 ON DBO.PromotionHeader
AFTER INSERT
AS
DECLARE #SUS_ID INT
SET #SUS_ID = (
SELECT Max(id)
FROM OrderSuspendRule
WHERE PromotionHeaderId = (
SELECT PreviousId
FROM inserted
WHERE ID = SCOPE_IDENTITY()
)
AND ISACTIVE=1
)
IF (#SUS_ID IS NOT NULL) --**VARIABLE IS ALWAYS NULL NO MATTER WHAT**
BEGIN
INSERT INTO OrderSuspendRule (
Guid
,CreatedAt
,UpdatedAt
,IsActive
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,PromotionHeaderId
,SuspendTypeId
,OfferItemId
)
SELECT NEWID()
,GETDATE()
,GETDATE()
,1
,CompanyId
,UpdatedById
,CreatedById
,SuspendFrom
,SuspendTo
,SCOPE_IDENTITY()
,SuspendTypeId
,OfferItemId
FROM OrderSuspendRule
WHERE id = #SUS_ID
END
Inside a for insert trigger, you can assume that all rows in the inserted table were inserted. There is no need to double check this with scope_identity().
To explain why scope_identity() is null, remember that scope_identity() returns the last inserted identity in the current scope. Since your trigger runs in its own scope, this will always be null, unless the trigger itself performs an insert.
Also, be aware that your trigger can be run for an insert of multiple rows. That means you can't expect only a single #sus_id, there might be many.

Resources