I created a trigger as below
alter trigger sale_Trigger_Update on sale
after update
as
begin
Declare #old_value varchar(50)
Declare #new_value varchar(50)
Declare #sale_id UNIQUEIDENTIFIER
DECLARE new_cur CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT saleid
FROM INSERTED
open new_cur
Fetch Next from new_cur into #sale_id
while ##FETCH_STATUS = 0
Begin
set #old_value = (select enddate from deleted where SaleID = #sale_id)
set #new_value = (select enddate from inserted where SaleID = #sale_id)
insert into zzz (old_value,new_value) values(#old_value,#new_value)
end
CLOSE new_cur
DEALLOCATE new_cur
end
Then I do an update statement as below
update sale
set enddate = null
Sale table contain only 2 rows
and the execution is continuing unlimited.
I tried
update sale
set enddate = null
where saleid = 10
same problem.
Then I forcefully stopped the execution. Then checked the sale table and zzz table. No changes happened.
I am sure there is some issue in cursor. Can somebody show some light on it.
****Edited****
Actually I need to check enddate in deleted is null and enddate in inserted is not null
open new_cur
Fetch Next from new_cur into #sale_id
while ##FETCH_STATUS = 0
Begin
set #old_value = (select enddate from deleted where SaleID = #sale_id)
set #new_value = (select enddate from inserted where SaleID = #sale_id)
if #old_value = null and #new_value != null
begin
SELECT approval.*,
(select diag.*
from diag diag
where approval.id =diag.id
FOR XML PATH('diag'), TYPE
),
(select ser.*
from ser ser
where approval.id =ser.id
FOR XML PATH('ser'), TYPE
)
FROM approval approval,
where approval.id = 1
and approval.saleid =#saleid
FOR XML PATH, ELEMENTS,
root('Head')
end if
end
CLOSE new_cur
DEALLOCATE new_cur
Regarding the cursor.
Could you replace your trigger with?
alter trigger sale_Trigger_Update on sale
after update
as
begin
insert into zzz (old_value,new_value)
select
--i.SalesID,
d.enddate,
i.enddate
from inserted i
inner join deleted d on
i.SaleID = d.SaleID
where
d.enddate is null and
i.enddate is not null
end
Related
Hello I made a cursor that check every row and check the status of the Caseinventory and then sets the status on the caseStatus. Problem is if I set the last counted row to 601 and it changes the code to 3 (shown in the code) it will not check if the rest is 601 and then just set the status to 3 even though rest is 600 or 599.
How can I solve this? should I count the amount on the CaseInventory and then check the status on every counted row and then update if the value is 601? and how do you do this? first time using cursor.
ALTER PROCEDURE [dbo].[SetsStatusOnCaseStatusByCaseInventoryStatus]
#StatusID INT,
#ID int,
#CaseInventoryID int
AS
BEGIN
--find caseID on CaseInventoryID
declare #CaseID int = (SELECT CaseID from [dbo].[factCaseInventory] where ID=#ID)
--CaseStatus
declare #CaseIkkeStartet int =1
declare #CaseIgangStatus int = 2
declare #CaseDoneStatus int = 3
--CaseInventoryStatus
DECLARE #NotStarted int = 599
DECLARE #Nocode int = 600
declare #YesCode int = 601
--Cursor
declare CaseInventoryCursor cursor for
Select ID, StatusID, CaseID from [dbo].[factCaseInventory]
open CaseInventoryCursor
fetch next from CaseInventoryCursor into #ID, #StatusID, #CaseID
while(##FETCH_STATUS = 0)
begin
SELECT #StatusID =StatusID from [dbo].[factCaseInventory] where CaseID=#CaseID
if(#StatusID = #Nocode)
begin
update [dbo].[factCase] set CaseStatusID=#CaseDoneStatus where CaseId=#CaseID
end
else if (#StatusID =#YesCode)
begin
update [dbo].[factCase] set CaseStatusID=#CaseIgangStatus where CaseId=#CaseID
end
else if (#StatusID =#NotStarted)
begin
update [dbo].[factCase] set CaseStatusID=#CaseIkkeStartet where CaseId=#CaseID
end
fetch next from CaseInventoryCursor into #ID, #StatusID, #CaseID
end
close CaseInventoryCursor
deallocate CaseInventoryCursor
END
It's unclear to me why you're passing parameters to your stored procedure when you then either don't use them, or overwrite them with new values before you use them.
However, if what you're trying to do is update each factCase record with a new CaseStatusID value based upon the related factCaseInventory record's StatusID value, you can just do a simple UPDATE with a join:
update fc set CaseStatusID =
case fci.StatusID
when 600 then 3
when 601 then 2
when 599 then 1
end
from factCaseInventory fci
left join factCase fc on fvi.CaseID = fc.CaseID
i have couple of stored procedures in our sql server 2008 that gets executed one after another, im facing a sql error (Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding) at the last one which updates or inserts against a 1 million records table while it works with smaller files but when updating or inserting this number of records it stops and throws that error , how can i tune the stored procedure, i tried to clean the server with sp_updatestates but no luck, i'd appreciate any help, thank you, here is my stored procedure code :
USE [DBTest]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_UpdateData]
#ItemsExt dbo.TT_ItemsExt READONLY
AS
BEGIN
SET NOCOUNT ON;
Declare
#ItemID nvarchar(36)
,#DescE nvarchar(50)
,etc
,etc
---------------------------------------
DECLARE Item_cursor CURSOR FOR
SELECT
ItemID
,DescE
,etc
,etc
FROM #ItemsExt
OPEN Item_cursor
FETCH NEXT FROM Item_cursor INTO
#ItemID
,#DescE
,etc
,etc
WHILE ##FETCH_STATUS = 0
BEGIN
If exists (select * from Items WHERE ItemID = #ItemID)
Begin
Update dbo.Items SET
DescEng = #DescE
,etc = #etc
WHERE ItemID = #ItemID
End
ElSE
Begin
Declare #ItemNumber nvarChar(36)
SET #ItemNumber = REPLACE((REPLACE(RIGHT(#ItemID, LEN(#ItemID) - 1))
insert into dbo.Items
(ItemID , etc)
VALUES (#ItemID, etc)
End
--ItemsReplacment Table
If exists (select * from dbo.ItemsReplacement WHERE ItemID = #ItemID)
Begin
Update dbo.ItemsReplacement SET
ItemAlter= #AlternativeItem
,etc
WHERE ItemID = #ItemID
End
ElSE
Begin
Insert Into dbo.ItemsReplacement Values (
#ItemID
,etc
,etc)
End
FETCH NEXT FROM Item_cursor INTO
#ItemID
,#DescE
,etc
,etc
END
CLOSE Item_cursor;
DEALLOCATE Item_cursor;
END
ps: the timeout of execution is set to 0 meaning infinite.
Here's a better aproach to those updates and inserts:
ALTER PROCEDURE [dbo].[SP_UpdateData]
#ItemsExt dbo.TT_ItemsExt READONLY
AS
BEGIN
UPDATE IT
SET
DescEng = ITE.DescE,
etc = ITE.etc
FROM
dbo.Items IT
INNER JOIN ItemsExt ITE ON (IT.ItemID = ITE.ItemID);
INSERT INTO
dbo.Items
(
ItemID,
etc
)
SELECT
ITE.ItemID,
ITE.etc
FROM
dbo.ItemsExt ITE;
WHERE
NOT EXISTS (SELECT 1 FROM dbo.Items WHERE ItemID = ITE.ItemID);
UPDATE ITR
SET
ItemAlt = 0, --AlternativeItem?
etc = ITE.etc
FROM
dbo.ItemsReplacement ITR
INNER JOIN ItemsExt ITE ON (ITR.ItemID = ITE.ItemID);
INSERT INTO
dbo.ItemsReplacement
(
ItemID,
etc
)
SELECT
ITE.ItemID,
ITE.etc
FROM
dbo.ItemsExt ITE;
WHERE
NOT EXISTS (SELECT 1 FROM dbo.ItemsReplacement WHERE ItemID = ITE.ItemID);
END
That way you avoid unecessary cursor-loops and let the Database do it's magic on planning the execution.
And by the way: Shouldn't it have any kind of "restriction" on those updates and inserts? Are you planning on run all those tables on all executions?
!! On your original code you were using #AlternativeItem that I couldn't find anywhere. Used 0 in my response.
I have 2 triggers for 2 tables. Both create numbers, one creates an articlenumber, the other an ordernumber.
Articlenumber:
ALTER TRIGGER [dbo].[trg_CreateArtikelnummer]
ON [dbo].[tblArtikel]
AFTER INSERT
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
DECLARE #ArtikelID INT;
DECLARE #LfID INT; --supplierID
DECLARE #ProdID INT
-- ---Wein, Traubensaft, Sekt
SET #ArtikelID = (SELECT
CASE WHEN Artikelart = 'Wein' THEN 1
WHEN Artikelart = 'Traubensaft' THEN 2
WHEN Artikelart = 'Sekt' THEN 3
END AS ArtikelID
FROM inserted)
SET #LfID = (SELECT Lfid FROM inserted)
SET #ProdID = (SELECT ProdID FROM inserted)
SET #Prodid = (SELECT COUNT(ProdID)
FROM [dbo].[tblArtikel]
WHERE Lfid = #LfID)
UPDATE dbo.tblArtikel
SET Artikelnummer = CONVERT(INT, CONVERT(NCHAR(1), #ArtikelID) +
CONVERT(NCHAR(3), FORMAT(#LfID, '000')) + CONVERT(NCHAR(4), FORMAT(#ProdID,'0000')))
FROM inserted
INNER JOIN dbo.tblArtikel ON inserted.prodid = dbo.tblArtikel.prodid
This one fires IMMEDIATELY when inserting date in the table by hand (for test purpose) and displays the generated articlenumber instantely.
Ordernumber: should be build from the running number per year for each supplier, so 1703001 stands for year 2017, supplierID 03, ordernumber 001
ALTER TRIGGER [dbo].[trg_CreateBestellnummer]
ON [dbo].[tblOrders]
AFTER INSERT
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
--set nocount on;
declare #Jahr int
declare #LfID int --supplierID
declare #OrderID int
declare #Order int
--declare #Bestelldatum date
set #Jahr=(select right(year(getdate()),2))
set #LfID=(SELECT Lfid from inserted)
--SELECT #OrderID=INSERTED.OrderID from inserted
set #Order=(SELECT count(OrderID) from [dbo].[tblOrders] where Lfid=#LfID and year(Bestelldatum)=Year(Getdate()))
Update dbo.tblOrders set Bestellnummer=convert(int,convert(nchar(2),#Jahr)+convert(nchar(2),FORMAT(#LfID, '00'))+convert(nchar(3),format(#Order,'000')))
from INSERTED inner join dbo.tblOrders on INSERTED.OrderID= dbo.tblOrders.OrderID
This one does NOT fire at once, when finishing inserting date into the table and does NOT display the generated ordernumber.
I get a red exclamation mark on the left side of the records saying something like "data are write protected. Execute query again".
When I click the execute button on the menue the the trigger fires and updates the correct ordernumber.
What can be the reason?
Thanks
Michael
Update
the purpose of this excerise is to eliminate passing the #RegModifiedDateTime again what i want is i should be able to read ModifiedDateTime by passing Id
example: if i pass Id = 564 then i should be able to read
`schoold_Id and ModifiedDateTime`
end update
here is how my table looks like for SchoolRegistration:
school_id id active modifydatetime
--------------------------------------------------
432 564 1 2008-12-14 13:15:38.750
342 564 1 2008-12-14 14:15:50.470
353 564 1 2008-12-14 14:19:46.703
end update
how do i loop to update my SchoolRegistration table? the id might have 1 or many rows in the SchoolRegistration but the thing is that RegModifiedDateTime is a unique for concurrency purpose and i should loop to get the right modifydatetime for that id.
alter procedure [dbo].[del_schoolRegistration]
#Id bigint,
#RegModifiedDateTime datetime
as
begin
declare #rowsAffected int
begin tran
--registration
update SchoolRegistration
set Active = 0,
ModifiedDateTime = getdate()
where (Id = #Id and RegModifiedDateTime = #RegModifiedDateTime or #RegModifiedDateTime is null )
if (#rowsAffected < 1) begin
rollback tran
end
else begin
commit tran
end
return #rowsAffected
end
--registration
;with tmp as (
select *, rn=ROW_NUMBER() over (partition by ID order by RegModifiedDateTime desc)
from SchoolRegistration
where (Id = #Id and RegModifiedDateTime = #RegModifiedDateTime or #RegModifiedDateTime is null ))
update tmp
set Active = 0,
ModifiedDateTime = getdate()
WHERE rn=1
What happens here is that if you did not know the RegModifiedDateTime you are looking for (by passing #RegModifiedDateTime as NULL), the query will catch them all for the ID due to #RegModifiedDateTime is null, but update ONLY the LATEST RegModifiedDateTime based on the row_numbering and CTE table definition.
EDIT
The above query retains the option to pass in a direct #RegModifiedDateTime should a record other than the latest need updating. To always update only the latest, drop the WHERE filter against #RegModifiedDateTime completely
--registration
;with tmp as (
select *, rn=ROW_NUMBER() over (partition by ID order by RegModifiedDateTime desc)
from SchoolRegistration
where Id = #Id)
update tmp
set Active = 0,
ModifiedDateTime = getdate()
WHERE rn=1
i endup using curor:
USE AdventureWorks
GO
DECLARE #ProductID INT
DECLARE #getProductID CURSOR
SET #getProductID = CURSOR FOR
SELECT ProductID
FROM Production.Product
OPEN #getProductID
FETCH NEXT
FROM #getProductID INTO #ProductID
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #ProductID
FETCH NEXT
FROM #getProductID INTO #ProductID
END
CLOSE #getProductID
DEALLOCATE #getProductID
GO
Before I go any further: Yes, I know that cursors perform poorly compared with set-based operations. In this particular case I'm running a cursor on a temporary table of 100 or so records, and that temporary table will always be fairly small, so performance is less crucial than flexibility.
My difficulty is that I'm having trouble finding an example of how to update a column fetched by a cursor. Previously when I've used cursors I've retrieved values into variables, then run an update query at each step based upon these values. On this occasion I want to update a field in the temporary table, yet I can't figure out how to do it.
In the example below, I'm trying to update the field CurrentPOs in temporary table #t1, based upon a query that uses #t1.Product_ID to look up the required value. You will see in the code that I have attempted to use the notation curPO.Product_ID to reference this, but it doesn't work. I have also attempted to use an update statement against curPO, also unsuccessfully.
I can make the code work by fetching to variables, but I'd like to know how to update the field directly.
I think I'm probably missing something obvious, but can anyone help?
declare curPO cursor
for select Product_ID, CurrentPOs from #t1
for update of CurrentPOs
open curPO
fetch next from curPO
while ##fetch_status = 0
begin
select OrderQuantity = <calculation>,
ReceiveQuantity = <calculation>
into #POs
from PurchaseOrderLine POL
inner join SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
inner join PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
where Product_ID = curPO.Product_ID
and SA.AddressType = '1801'
update curPO set CurrentPOs = (select sum(OrderQuantity) - sum(ReceiveQuantity) from #POs)
drop table #POs
fetch next from curPO
end
close curPO
deallocate curPO
After doing a bit more googling, I found a partial solution. The update code is as follows:
UPDATE #T1
SET CURRENTPOS = (SELECT SUM(ORDERQUANTITY) - SUM(RECEIVEQUANTITY)
FROM #POS)
WHERE CURRENT OF CURPO
I still had to use FETCH INTO, however, to retrieve #t1.Product_ID and run the query that produces #POs, so I'd still like to know if it's possible to use FETCH on it's own.
Is this what you want?
declare curPO cursor
for select Product_ID, CurrentPOs from #t1
for update of CurrentPOs
open curPO
fetch next from curPO
while ##fetch_status = 0
begin
update curPO set CurrentPOs =
(select sum(<OrderQuantityCalculation>)
from PurchaseOrderLine POL
inner join SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
inner join PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
where Product_ID = curPO.Product_ID
and SA.AddressType = '1801') -
(select sum(<ReceiveQuantityCalculation>)
from PurchaseOrderLine POL
inner join SupplierAddress SA ON POL.Supplier_ID = SA.Supplier_ID
inner join PurchaseOrderHeader POH ON POH.PurchaseOrder_ID = POL.PurchaseOrder_ID
where Product_ID = curPO.Product_ID
and SA.AddressType = '1801')
fetch next from curPO
end
close curPO
deallocate curPO
Maybe you need something like that:
update DataBaseName..TableName
set ColumnName = value
where current of your_cursor_name;
Here's an example to calculate one column based upon values from two others (note, this could be done during the original table select). This example can be copy / pasted into an SSMS query window to be run without the need for any editing.
DECLARE #cust_id INT = 2, #dynamic_val NVARCHAR(40), #val_a INT, #val_b INT
DECLARE #tbl_invoice table(Cust_ID INT, Cust_Fees INT, Cust_Tax INT)
INSERT #tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 1, 111, 11
INSERT #tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 2, 222, 22
INSERT #tbl_invoice ( Cust_ID, Cust_Fees, Cust_Tax ) SELECT 3, 333, 33
DECLARE #TblCust TABLE
(
Rec_ID INT
, Val_A INT
, Val_B INT
, Dynamic_Val NVARCHAR(40)
, PRIMARY KEY NONCLUSTERED (Rec_ID)
)
INSERT #TblCust(Rec_ID, Val_A, Val_B, Dynamic_Val)
SELECT Rec_ID = Cust_ID, Val_A = Cust_Fees, Val_B = Cust_Tax, NULL
FROM #tbl_invoice
DECLARE cursor_cust CURSOR FOR
SELECT Rec_ID, Val_A, Val_B, Dynamic_Val
FROM #TblCust
WHERE Rec_ID <> #cust_id
FOR UPDATE OF Dynamic_Val;
OPEN cursor_cust;
FETCH NEXT FROM cursor_cust INTO #cust_id, #val_a, #val_b, #dynamic_val;
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #TblCust
SET Dynamic_Val = N'#c = "' + LTRIM(STR((#val_a + #val_b), 40)) + N'"'
WHERE CURRENT OF cursor_cust
FETCH NEXT FROM cursor_cust INTO #cust_id, #val_a, #val_b, #dynamic_val;
END
CLOSE cursor_cust
DEALLOCATE cursor_cust
SELECT * FROM #TblCust