WHILE loop doesnt ends in UPDATE command TSQL - sql

I have one TSQL command that I use in SSIS sequence container in Execute sql task executable.
I need to insert data into archive DB/table that is same as PROD and attached on same instance of SQL 2016 standard edt.
I insert about 5671294 records and in the mean time update the flag in other help/stage table that use in further checks.
My Loop never ends after updating last 71294 records. On Activity monitor i can see stuck in UPDATE command and no columns for updating.
Another problem is that this works very slow (DB in simple recovery).
SET IDENTITY_INSERT ArchiveDB..Table ON
GO
DECLARE #InsertRowCnt INT
SET #InsertRowCnt = 1
DECLARE #InsertBatchSize INT
SET #InsertBatchSize=100000
WHILE (#InsertRowCnt > 0)
BEGIN
DECLARE #OutputTbl AS TABLE(BSGID INT)
INSERT INTO ArchiveDB..Table ([BSGID], [Identifier],[SystemCombinationItems], [TotalItems], [TotalBankers],[SpecialCombinationID], [ModifiedOn], [ModifiedBy], [CreatedOn])
OUTPUT inserted.BSGID INTO #OutputTbl(BSGID)
SELECT TOP (#InsertBatchSize) s.BSGID, s.Identifier,
s.SystemCombinationItems, s.TotalItems, s.TotalBankers, s.SpecialCombinationID, s.ModifiedOn,
s.ModifiedBy, s.CreatedOn
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----Updating stage table contains 2 columns BSGID int,IsInserted int null
UPDATE arh
SET arh.IsInserted=1
FROM ArchiveDB.dbo.StageTable AS arh WITH(NOLOCK)
INNER JOIN #OutputTbl AS a
ON arh.BSGID=a.BSGID
WHERE arh.IsInserted is null
SET #InsertRowCnt = ##ROWCOUNT;
END
SET IDENTITY_INSERT ArchiveDB..Table OFF
GO

SET IDENTITY_INSERT ArchiveDB..Table ON
GO
DECLARE #InsertRowCnt INT
--------------------------------
SELECT #InsertRowCnt=COUNT(*)
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----------------------------
DECLARE #InsertBatchSize INT
SET #InsertBatchSize=100000
WHILE (#InsertRowCnt > 0)
BEGIN
DECLARE #OutputTbl AS TABLE(BSGID INT)
INSERT INTO ArchiveDB..Table ([BSGID], [Identifier],[SystemCombinationItems],
[TotalItems], [TotalBankers],[SpecialCombinationID], [ModifiedOn], [ModifiedBy],
[CreatedOn])
OUTPUT inserted.BSGID INTO #OutputTbl(BSGID)
SELECT TOP (#InsertBatchSize) s.BSGID, s.Identifier,
s.SystemCombinationItems, s.TotalItems, s.TotalBankers, s.SpecialCombinationID,
s.ModifiedOn,
s.ModifiedBy, s.CreatedOn
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----Updating stage table contains 2 columns BSGID int,IsInserted int null
UPDATE arh
SET arh.IsInserted=1
FROM ArchiveDB.dbo.StageTable AS arh WITH(NOLOCK)
INNER JOIN #OutputTbl AS a
ON arh.BSGID=a.BSGID
WHERE arh.IsInserted is null
--------------------------------
SELECT #InsertRowCnt=COUNT(*)
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----------------------------
END
SET IDENTITY_INSERT ArchiveDB..Table OFF
GO

Related

SQL - After insert Same Table

So I understand recursive triggers. Got to be careful of deadlocks etc. However this is only after an insert not after insert and update. Also, I have an audit trigger table that I am updating to make sure all is well. And querying after to double check. All looks fine but no update happens.
if exists (select 'a' from sys.triggers where name = 'invoicememologic')
begin
drop trigger invoicememologic
end
go
create trigger invoicememologic
on transactiontable
after insert
as
begin
declare #inum varchar(1000)
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum) b
while #inum is not null
begin
declare #rCount int
select #rCount = count(*)
from transactiontable
where TransactionInvNum = #inum
if #rCount = 1
begin
declare #tid varchar(100)
select #tid = transactionid
from transactiontable
where TransactionInvNum = #inum
declare #pmemo varchar(MAX)
select #pmemo = p.projectMemo
from transactiontable tt
inner join project p on left(tt.projectid, charindex(':', tt.projectid)) = p.projectid
where transactionInvNum = #inum
insert into audittrigger
values (#pmemo, #tid)
update transactiontable
set transactionmemo2 = #pmemo
where ltrim(rtrim(transactionid)) = ltrim(rtrim(#tid))
end
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where abs(transactionjointinv) = 1
and right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum ) a
where transactioninvnum > #inum
end
end
Reason for trigger. 1 Invoice can be multiple rows in the database. 3 rows. So it only should update any one of the 3 rows. Doesn't matter. And it must grab the memo from the parent project of the phases that are being inserted into the database. hence the inner join on the left with charindex.
So I check the audit table. All looks well there. MEMO is there and the transactionid is there. I query after the trigger fires. TransactionID exists in the transactiontable but the memo2 is not being updated.
TransactionMemo2 is type of ntext. I thought it might be varchar with a direct update command will fail. I tried to update manually through setting a var as the text string and call the update manually with the transactionid being required. all worked fine. I am lost

Alternative to Iteration for INSERT SELECT UPDATE in a sequence

I have a table with around 17k unique rows for which I need to run these set of statements in sequence
INSERT INTO TABLE1 using MASTERTABLE data (MASTERTABLE have 6 column)
SELECT value of column ID (Primary Key) of newly inserted row from TABLE1
Update that ID value in TABLE2 using a Stored Procedure
I have tried:
while loop: took around 3 hours to complete the execution
cursor: cancelled the query after executing it overnight
In my understanding I can not use JOIN as I need to execute the statements in a sequence
The questions is not detailed enough. The general idea I would like to use something like this
-- create a output table to hold new id, and key columns to join later
DECLARE #OutputTbl TABLE (ID INT, key_Columns in MASTERTABLE)
INSERT INTO TABLE1
OUTPUT INSERTED.ID, MASTERTABLE.key_columns INTO #OutputTbl
SELECT *
FROM MASTERTABLE
UPDATE T2
SET ID = o.ID
FROM TABLE2 t2
INNER JOIN OutputTbl o
ON t2.key_column = o.key_column
Maybe you can consider a TRIGGER on TABLE1 from which to call the stored procedure on TABLE2, and then you can call your INSERT as you wish/need.. one by one or in blocks..
DROP TRIGGER TR_UPD_TABLE2
GO
CREATE TRIGGER TR_UPD_TABLE2 ON TABLE1 AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #columnID INT = NULL
IF (SELECT COUNT(*) FROM INSERTED)=1 BEGIN
-- SINGLE INSERT
SET #columnID = (SELECT columnID FROM INSERTED)
EXEC TableTwoUpdateProcedure #columnID
END ELSE BEGIN
-- MASSIVE INSERT (IF NEEDED)
SET #columnID = 0
WHILE #columnID IS NOT NULL BEGIN
SET #columnID = (SELECT MIN(columnID) FROM INSERTED WHERE columnID > #columnID)
IF #columnID IS NOT NULL BEGIN
EXEC TableTwoUpdateProcedure #columnID
END
END
END
END

Handling muliple rows with insert trigger

I have a trigger on an orders table that inserts order details into the order_details table. This works when only one row is entered, but not when multiple rows are inserted at once. I have read multiple threads > on various sites about using a cursor, while statements, temp tables, etc. I have tried a few but no success. Any suggestions on the BEST/Easiest way to ensure I get all the detailed rows added when an order is placed.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[CreateOrderDetails]
ON [dbo].[Orders]
FOR INSERT
AS
declare #OrderStagingId uniqueidentifier
declare #OrderType as varchar(50)
declare #OrderID uniqueidentifier
declare #LocationID uniqueidentifier
declare #status as varchar (25)
declare #vendorid as uniqueidentifier
SELECT #OrderID = OrderID, #LocationID = LocationID,
#vendorid = vendorid, #OrderStagingId = OrderStagingId
FROM inserted
Begin
SET NOCOUNT ON;
-- Insert statements for trigger here
Insert into [Brain].dbo.[Order_Details]
SELECT
NEWID() AS OrderDetailId, #OrderID AS OrderId,
dbo.OrderStaging_Details.EcProductID,
dbo.OrderStaging_Details.Qty, dbo.OrderStaging_Details.Qty_Type,
dbo.OrderStaging_Details.Cost, dbo.OrderStaging_Details.Ext_Cost,
dbo.OrderStaging_Details.EnteredBy,
NULL AS ReceivedBy, NULL AS ReceivedDate, NULL AS ReceivedQty,
dbo.OrderStaging_Details.OrderNote, NULL AS ReceivedNote,
dbo.OrderStaging_Details.UpdatedBy, dbo.OrderStaging_Details.UpdateDate
FROM
dbo.Vendor_Assigned_Locations
INNER JOIN
dbo.Vendor_Contacts
INNER JOIN
dbo.Vendors
INNER JOIN
dbo.OrdersStaging
INNER JOIN
dbo.OrderStaging_Details ON dbo.OrdersStaging.OrderStagingID = dbo.OrderStaging_Details.OrderStagingID
ON dbo.Vendors.VendorID = dbo.OrderStaging_Details.VendorId
ON dbo.Vendor_Contacts.Vendor_ID = dbo.Vendors.VendorID
ON dbo.Vendor_Assigned_Locations.LocationID = dbo.OrdersStaging.LocationID
AND dbo.Vendor_Assigned_Locations.VCID = dbo.Vendor_Contacts.VCID
INNER JOIN
dbo.Orders ON dbo.OrdersStaging.OrderStagingID = dbo.Orders.OrderStagingID
AND dbo.Vendors.VendorID = dbo.Orders.VendorID
AND dbo.Vendor_Contacts.VCID = dbo.Orders.VendorContactID
AND dbo.OrdersStaging.LocationID = dbo.Orders.LocationID
LEFT OUTER JOIN
dbo.Order_Details ON dbo.Orders.OrderID = dbo.Order_Details.OrderID
WHERE
(dbo.OrderStaging_Details.OrderStagingID = #OrderStagingID)
AND (dbo.OrdersStaging.LocationID = #Locationid)
AND (dbo.Vendors.VendorID = #Vendorid)
AND (dbo.Order_Details.OrderID IS NULL)
end
This trigger is probably necessary to you
ALTER TRIGGER [dbo].[CreateOrderDetails] ON [dbo].[Orders]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for trigger here
INSERT INTO [Brain].dbo.[Order_Details]
SELECT
NEWID() AS OrderDetailId, i.OrderID AS OrderId,
dbo.OrderStaging_Details.EcProductID,
dbo.OrderStaging_Details.Qty, dbo.OrderStaging_Details.Qty_Type,
dbo.OrderStaging_Details.Cost, dbo.OrderStaging_Details.Ext_Cost,
dbo.OrderStaging_Details.EnteredBy,
NULL AS ReceivedBy, NULL AS ReceivedDate, NULL AS ReceivedQty,
dbo.OrderStaging_Details.OrderNote, NULL AS ReceivedNote,
dbo.OrderStaging_Details.UpdatedBy, dbo.OrderStaging_Details.UpdateDate
FROM
dbo.OrdersStaging
INNER JOIN
dbo.OrderStaging_Details ON dbo.OrdersStaging.OrderStagingID = dbo.OrderStaging_Details.OrderStagingID
INNER JOIN
dbo.Vendors ON dbo.Vendors.VendorID = dbo.OrderStaging_Details.VendorId
INNER JOIN
dbo.Vendor_Contacts ON dbo.Vendor_Contacts.Vendor_ID = dbo.Vendors.VendorID
INNER JOIN
dbo.Vendor_Assigned_Locations ON dbo.Vendor_Assigned_Locations.LocationID = dbo.OrdersStaging.LocationID
AND dbo.Vendor_Assigned_Locations.VCID = dbo.Vendor_Contacts.VCID
INNER JOIN
dbo.Orders ON dbo.OrdersStaging.OrderStagingID = dbo.Orders.OrderStagingID
AND dbo.Vendors.VendorID = dbo.Orders.VendorID
AND dbo.Vendor_Contacts.VCID = dbo.Orders.VendorContactID
AND dbo.OrdersStaging.LocationID = dbo.Orders.LocationID
INNER JOIN
inserted i ON dbo.Orders.OrderID = i.OrderID
END
The outer select statement where you are picking up #OrderID, #LocationID etc, is only picking the first row out of inserted which contains all the orders that are being inserted in the transaction that pulled the trigger.
Use insert into detail(...)
Select NewID(), Inserted.OrderID, ...
From Inserted
inner join ...
Where etc
instead.
It's because you're thinking about rows rather than columns. Dump the #variables, and join on inserted.

SQL: Query timeout expired

I have a simple query for update table (30 columns and about 150 000 rows).
For example:
UPDATE tblSomeTable set F3 = #F3 where F1 = #F1
This query will affected about 2500 rows.
The tblSomeTable has a trigger:
ALTER TRIGGER [dbo].[trg_tblSomeTable]
ON [dbo].[tblSomeTable]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
declare #operationType nvarchar(1)
declare #createDate datetime
declare #UpdatedColumnsMask varbinary(500) = COLUMNS_UPDATED()
-- detect operation type
if not exists(select top 1 * from inserted)
begin
-- delete
SET #operationType = 'D'
SELECT #createDate = dbo.uf_DateWithCompTimeZone(CompanyId) FROM deleted
end
else if not exists(select top 1 * from deleted)
begin
-- insert
SET #operationType = 'I'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
else
begin
-- update
SET #operationType = 'U'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
-- log data to tmp table
INSERT INTO tbl1
SELECT
#createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM (Select 1 as temp) t
LEFT JOIN inserted i on 1=1
LEFT JOIN deleted d on 1=1
END
And if I execute the update query I have a timeout.
How can I optimize a logic to avoid timeout?
Thank you.
This query:
SELECT *
FROM (
SELECT 1 AS temp
) t
LEFT JOIN
INSERTED i
ON 1 = 1
LEFT JOIN
DELETED d
ON 1 = 1
will yield 2500 ^ 2 = 6250000 records from a cartesian product of INSERTED and DELETED (that is all possible combinations of all records in both tables), which will be inserted into tbl1.
Is that what you wanted to do?
Most probably, you want to join the tables on their PRIMARY KEY:
INSERT
INTO tbl1
SELECT #createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM INSERTED i
FULL JOIN
DELETED d
ON i.id = d.id
This will treat update to the PK as deleting a record and inserting another, with a new PK.
Thanks Quassnoi, It's a good idea with "FULL JOIN". It is helped me.
Also I try to update table in portions (1000 items in one time) to make my code works faster because for some companyId I need to update more than 160 000 rows.
Instead of old code:
UPDATE tblSomeTable set someVal = #someVal where companyId = #companyId
I use below one:
declare #rc integer = 0
declare #parts integer = 0
declare #index integer = 0
declare #portionSize int = 1000
-- select Ids for update
declare #tempIds table (id int)
insert into #tempIds
select id from tblSomeTable where companyId = #companyId
-- calculate amount of iterations
set #rc=##rowcount
set #parts = #rc / #portionSize + 1
-- update table in portions
WHILE (#parts > #index)
begin
UPDATE TOP (#portionSize) t
SET someVal = #someVal
FROM tblSomeTable t
JOIN #tempIds t1 on t1.id = t.id
WHERE companyId = #companyId
delete top (#portionSize) from #tempIds
set #index += 1
end
What do you think about this? Does it make sense? If yes, how to choose correct portion size?
Or simple update also good solution? I just want to avoid locks in the future.
Thanks

Writing a complex trigger

This posting is an update (with trigger code) from my earlier posting yesterday
I am using SQL Server 2000. I am writing a trigger that is executed when a field Applicant.AppStatusRowID
Table Applicant is linked to table Location, table Company & table AppStatus.
My issue is creating the joins in my query.
When Applicant.AppStatusRowID is updated, I want to get the values from Applicant.AppStatusRowID, Applicant.FirstName, Applicant.Lastname, Location.LocNumber, Location.LocationName, Company.CompanyCode, AppStatus.DisplayText
The joins would be :
Select * from Applicant A
Inner Join AppStatus ast on ast.RowID = a.AppStatusRowID
Inner Join Location l on l.RowID = a.LocationRowID
Inner Join Company c on c.RowID = l.CompanyRowID
This is to be inserted into an Audit table (fields are ApplicantID, LastName, FirstName, Date, Time, Company, Location Number, Location Name, StatusDisposition, User)
The trigger query is:
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock)
Inner join Test_App a with (nolock) on a.RowID = d.rowid
inner join location l with (nolock) on l.rowid = d.Locationrowid
inner join appstatus ast with (nolock) on ast.rowid = d.appstatusrowid
inner join company c with (nolock) on c.rowid = l.CompanyRowid
--Inner Join Deleted d ON a.RowID = d.RowID
--Where (a.rowid = #Approwid)
END
GO
Any ideas?
Try with this code
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock),location l with (nolock),appstatus ast with (nolock),company c with (nolock)
where d.Locationrowid =l.rowid and
d.appstatusrowid = ast.rowid and
c.rowid = l.CompanyRowid
END GO
whith this code you get the update-deleted row(the old_value)
see you.