How to increase cursor performance in sql - sql-server

i have used trigger for recalculation multiple data.
in trigger i have used cursor.in cursor i have used another sp's which is contain 1 more cursor.
so it's very slow for calculation of only 80 rows data. and it's take 20 second to calculate the data. so please give me any suggestion or solution for it quickly.
thanks
here is my trigger
/****** Object: Trigger [dbo].[tr_TransactionJournal_InsteadOfDelete] Script Date: 12/23/2015 3:13:09 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_TransactionJournal_InsteadOfDelete]
ON [dbo].[TransactionJournal]
INSTEAD OF DELETE
AS
SET NOCOUNT ON;
DECLARE
#TxNum INT,
#TxDate DATE
/*
declare #TempDetailTable to figure out which ChartAcctCode & Item needs to be recalculated
*/
DECLARE #TempDetailTable TABLE (
ChartAcctCode NVARCHAR(10),
ItemCode NVARCHAR(10)
)
DECLARE #MyCursor_1 CURSOR
SET #MyCursor_1 = CURSOR FAST_FORWARD
FOR
SELECT TxNum,TxDate FROM DELETED
OPEN #MyCursor_1
FETCH NEXT FROM #MyCursor_1
INTO #TxNum,#TxDate
WHILE ##fetch_status = 0 -- This is executed as long as the previous fetch succeeds.
BEGIN
------------------
BEGIN TRANSACTION
INSERT INTO #TempDetailTable
SELECT DISTINCT tjd.ChartAcctCode, tjd.ItemCode
FROM dbo.TransactionJournalDetail AS tjd
WHERE (tjd.TxNum = #TxNum)-- AND tjd.ItemCode IS NOT NULL
COMMIT TRANSACTION
/*
Actually deleting FROM TxJournal & TxJournalDetail
*/
DELETE FROM TransactionJournal WHERE TxNum=#TxNum
/*
Start Recalculating...
*/
DECLARE #MyChartAcctCode NVARCHAR(10)
DECLARE #MyItemCode NVARCHAR(10)
DECLARE #MyCursor CURSOR
/*
1st Loop to RecalcQAV if Detail has any ChartAcctCode = "AINV"
*/
SET #MyCursor = CURSOR FAST_FORWARD
FOR
SELECT ItemCode FROM #TempDetailTable WHERE ChartAcctCode = 'INV'
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyItemCode
WHILE ##fetch_status = 0
BEGIN
EXEC RecalcQAV #MyItemCode, #TxDate;
FETCH NEXT FROM #MyCursor
INTO #MyItemCode
END
/*
2nd Loop to RecalcAcctBalance if Detail has any ChartAcctCode = "AINV"
*/
SET #MyCursor = CURSOR FAST_FORWARD
FOR
SELECT ChartAcctCode FROM #TempDetailTable
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyChartAcctCode
WHILE ##fetch_status = 0
BEGIN
EXEC RecalcAcctBalance #MyChartAcctCode, #TxDate;
FETCH NEXT FROM #MyCursor
INTO #MyChartAcctCode
END
CLOSE #MyCursor;
DEALLOCATE #MyCursor;
------------------
FETCH NEXT FROM #MyCursor_1
INTO #TxNum,#TxDate
END
CLOSE #MyCursor_1;
DEALLOCATE #MyCursor_1;

ALTER PROCEDURE [dbo].[RecalcAcctBalance]
(
#ChartAcctCode NVARCHAR(10),
#BeginDate DATE
)
AS BEGIN
SET NOCOUNT ON;
DECLARE #LastDate DATE
SELECT #LastDate = MAX(tj.TxDate)
FROM dbo.TransactionJournal AS tj
JOIN dbo.TransactionJournalDetail AS tjd ON tj.TxNum = tjd.TxNum
WHERE tj.TxDate < #BeginDate
AND tjd.ChartAcctCode = #ChartAcctCode
DECLARE #MyTable TABLE (
AutoId INT IDENTITY(1,1) PRIMARY KEY,
TxDate DATE,
TxDetailId INT
)
INSERT INTO #MyTable (TxDate, TxDetailId)
SELECT tj.TxDate, tjd.AutoId
FROM dbo.TransactionJournal AS tj
JOIN dbo.TransactionJournalDetail AS tjd ON tj.TxNum = tjd.TxNum
WHERE tj.TxDate >= ISNULL(#LastDate, #BeginDate)
AND tjd.ChartAcctCode = #ChartAcctCode
ORDER BY tj.TxDate, tj.SourceDocOrder, tjd.AutoId
DECLARE
#LastDateMaxId INT,
#TxDetailId INT,
#LChartAcctBalance MONEY = 0
IF #LastDate IS NOT NULL
BEGIN
SELECT #TxDetailId = TxDetailId
FROM (
SELECT TxDetailId, RowNum = ROW_NUMBER() OVER (ORDER BY AutoId DESC)
FROM #MyTable
WHERE TxDate = #LastDate
) t
WHERE t.RowNum = 1
SELECT #LChartAcctBalance = ISNULL(ChartAcctBalance, 0)
FROM dbo.TransactionJournalDetail
WHERE AutoId = #TxDetailId
END
;WITH cte AS
(
SELECT *, val = #LChartAcctBalance + SUM(Amount) OVER (ORDER BY AutoId)
FROM dbo.TransactionJournalDetail
WHERE AutoId IN (
SELECT TxDetailId
FROM #MyTable
WHERE TxDate >= #BeginDate
)
)
UPDATE cte
SET ChartAcctBalance = val
END
the second sp is too large for refactoring... anyway the main problem - business logic with processing by item...

Related

Adding Column to stored procedure conditionally

I have the following (simplified) stored procedure:
CREATE FUNCTION [dbo].[UDF_FulfilmentBatch](#FulfilmentID INT) RETURNS
#Result TABLE (
[sequence] INT,
membershipid BIGINT,
membershipNo VARCHAR(255)
)
AS
BEGIN
DECLARE #_sequence INT
DECLARE #_membershipid BIGINT
DECLARE #_membershipNo VARCHAR(255)
SET #_sequence = 1
IF #FulfilmentID = 4
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_HH
END
IF #FulfilmentID = 3
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_ID
END
ELSE IF #FulfilmentID = 2
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_Art
END
ELSE
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_Tha
OPEN FulfilCursor
FETCH NEXT FROM FulfilCursor INTO #_membershipid, #_membershipNo
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT INTO #Result
VALUES (#_sequence, #_membershipid, #_membershipNo)
SET #_sequence = #_sequence + 1
FETCH NEXT FROM FulfilCursor INTO #_membershipid, #_membershipNo
END
CLOSE FulfilCursor
DEALLOCATE FulfilCursor
RETURN
END
GO
My problem is, that when FulfilmentID = 4, I wan to add an extra field - 'Delivery'
If have tried the following:
CREATE FUNCTION [dbo].[UDF_FulfilmentBatch](#FulfilmentID INT) RETURNS
#Result TABLE (
[sequence] INT,
membershipid BIGINT,
membershipNo VARCHAR(255)
IF #FulfilmentID = 4
BEGIN
,Delivery VARCHAR(255)
END
)
AS
BEGIN
DECLARE #_sequence INT
DECLARE #_membershipid BIGINT
DECLARE #_membershipNo VARCHAR(255)
IF #FulfilmentID = 4
BEGIN
DECLARE #_Delivery VARCHAR(255)
END
SET #_sequence = 1
IF #FulfilmentID = 4
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_HH
END
IF #FulfilmentID = 3
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_ID
END
ELSE IF #FulfilmentID = 2
BEGIN
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_Art
END
ELSE
DECLARE FulfilCursor CURSOR FAST_FORWARD FOR
SELECT * from VW_FulfilmentExtract_Tha
OPEN FulfilCursor
FETCH NEXT FROM FulfilCursor INTO #_membershipid, #_membershipNo
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT INTO #Result
VALUES (#_sequence, #_membershipid, #_membershipNo IF #FulfilmentID=4 BEGIN #_Delivery )
SET #_sequence = #_sequence + 1
FETCH NEXT FROM FulfilCursor INTO #_membershipid, #_membershipNo
END
CLOSE FulfilCursor
DEALLOCATE FulfilCursor
RETURN
END
GO
But this did not work (Please excuse any syntax errors, this is a rough typing for SO)
Form searching the web there does not seem to be much on this. Can it be done?
There are lots of issues in your code:
use SELECT *
use Cursor
...
In the end, it is overly complicated and looks at lot like Application code (C#, java, ...)
What you want to do should be done with a single Select (ie. on a set of data). Columns names should also be listed between SELECT and FROM.
One option is:
CREATE FUNCTION [dbo].[UDF_FulfilmentBatch](#FulfilmentID INT) RETURNS TABLE
AS
RETURN (
SELECT sequence = ROW_NUMBER() Over(Order By (Select 1))
, _membershipid, membershipNo, delivery
FROM VW_FulfilmentExtract_HH
WHERE #FulfilmentID = 4
UNION ALL
SELECT sequence = ROW_NUMBER() Over(Order By (Select 1))
, _membershipid, membershipNo, delivery = NULL
FROM VW_FulfilmentExtract_ID
WHERE #FulfilmentID = 3
UNION ALL
SELECT sequence = ROW_NUMBER() Over(Order By (Select 1))
, _membershipid, membershipNo, delivery = NULL
FROM VW_FulfilmentExtract_Art
WHERE #FulfilmentID = 2
UNION ALL
SELECT sequence = ROW_NUMBER() Over(Order By (Select 1))
, _membershipid, membershipNo, delivery = NULL
FROM VW_FulfilmentExtract_Tha
WHERE #FulfilmentID = not in (2, 3, 4)
);
Here I use ROW_NUMBER to generate your sequence number.
I added the Delivery column and set it to NULL when it is not needed.
You must update columns name. I just guess them.
By the way, this is not a Stored Procedure but a Inline User-Defined Functions

Invalid object name [dbo].[TriggerName]

I created a trigger named UPDATE_MERCHANDISE. The query completed with one error, a simple typo. I fixed the typo and changed the code to ALTER TRIGGER, but now when I try to execute it I receive the error
Invalid object name [dbo].[UPDATE_MERCHANDISE]
Why would it be valid when I created it, but not when I try to alter it? Any and all help/ideas would be appreciated.
Thanks.
USE [CIS31038]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[UPDATE_MERCHANDISE] ON [dbo].[ORDERITEM]
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
DECLARE #ITEMID VARCHAR(2)
DECLARE #TOTAL INT
--INSERT CASE
IF(EXISTS (SELECT * FROM INSERTED) AND NOT EXISTS (SELECT * FROM DELETED))
BEGIN
DECLARE INSERT_CURSOR CURSOR FOR
SELECT ITEMID, SUM(QUANTITY) AS TOTAL
FROM INSERTED
GROUP BY ITEMID
OPEN INSERT_CURSOR
FETCH NEXT FROM INSERT_CURSOR INTO #ITEMID, #TOTAL
WHILE(##FETCH_STATUS = 0)
BEGIN
UPDATE MERCHANDISE
SET QUANTITYONHAND = QUANTITYONHAND - #TOTAL
WHERE ITEMID = #ITEMID
FETCH NEXT FROM INSERT_CURSOR INTO #ITEMID, #TOTAL
END
CLOSE INSERT_CURSOR
DEALLOCATE INSERT_CURSOR
END
--DELETE CASE
IF(EXISTS (SELECT * FROM DELETED) AND NOT EXISTS (SELECT * FROM INSERTED))
BEGIN
DECLARE DELETE_CURSOR CURSOR FOR
SELECT ITEMID, SUM(QUANTITY) AS TOTAL
FROM DELETED
GROUP BY ITEMID
OPEN DELETE_CURSOR
FETCH NEXT FROM DELETE_CURSOR INTO #ITEMID, #TOTAL
WHILE (##FETCH_STATUS = 0)
BEGIN
UPDATE MERCHANDISE
SET QUANTITYONHAND = QUANTITYONHAND + #TOTAL
WHERE ITEMID = #ITEMID
FETCH NEXT FROM DELETE_CURSOR INTO #ITEMID, #TOTAL
END
CLOSE DELETE_CURSOR
DEALLOCATE DELETE_CURSOR
END
--UPDATE CASE
IF(EXISTS (SELECT * FROM INSERTED) AND EXISTS (SELECT * FROM DELETED))
BEGIN
DECLARE UPDATE_CURSOR CURSOR FOR
SELECT I.ITEMID, SUM(I.QUANTITY - D.QUANTITY) AS TOTAL
FROM INSERTED I INNER JOIN DELETED D ON
I.PONUMBER = D.PONUMBER AND I.ITEMID = D.ITEMID
GROUP BY I.ITEMID
OPEN UPDATE_CURSOR
FETCH NEXT FROM UPDATECURSOR INTO #ITEMID, #TOTAL
WHILE (##FETCH_STATUS = 0)
BEGIN
UPDATE MERCHANDISE
SET QUANTITYONHAND = QUANTITYONHAND - #TOTAL
WHERE ITEMID = #ITEMID
FETCH NEXT FROM UPDATE_CURSOR INTO #ITEMID, #TOTAL
END
CLOSE UPDATE_CURSOR
DEALLOCATE UPDATE_CURSOR
END
END
If you had a typo, most likely, the trigger never got created
Try CREATE TRIGGER instead of ALTER TRIGGER
By the way, generally, you remove the trigger and recreate, using something like.
IF Object_id('TRIGGER_NAME') is not null
DROP TRIGGER Trigger Name
GO
CREATE TRIGGER TriggerName
AS

optimize cursor query in sql server

We have a database with more the 10,000,000 records. The following statements is used to replace some of the words in each record with a different word from another table, but because there are so many records the execution doesn't even finish in a full day, is there anyway it can be optimized?
DECLARE My_Cursor CURSOR
FOR
SELECT
full_santance
, id
FROM
dbo.combined
WHERE
id BETWEEN 9000000 AND 10000000
DECLARE My_Cursor_r CURSOR
FOR
SELECT
old
, new
FROM
dbo.changesTable
DECLARE #full_santance varchar(500)
DECLARE #id numeric(18, 0)
DECLARE #word_old varchar(500)
DECLARE #word_new varchar(500)
DECLARE #corrected varchar(500)
DECLARE #r_word varchar(500)
OPEN My_Cursor
FETCH NEXT FROM My_Cursor INTO #full_santance, #id
WHILE ##FETCH_STATUS = 0
BEGIN
SET #corrected = #full_santance
OPEN My_Cursor_r
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
WHILE ##FETCH_STATUS = 0
BEGIN
IF #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old),'_', '') + '[^a-z]%'
OR #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old), '_', '')
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','') + '[^a-z]%'
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','')
BEGIN
SET #corrected = REPLACE(#corrected,REPLACE(RTRIM(#word_old),'_', ' '),REPLACE(RTRIM(#word_new),'_', ' '))
END
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
END
CLOSE My_Cursor_r
IF #corrected <> #full_santance
BEGIN
UPDATE
dbo.combined
SET
full_santance = #corrected
WHERE
id = #id
END
FETCH NEXT FROM My_Cursor INTO #word, #id
END
CLOSE My_Cursor
DEALLOCATE My_Cursor
DEALLOCATE My_Cursor_r
DECLARE #combined TABLE (id int, full_santance varchar(max))
INSERT #combined VALUES
(1, 'the quick brown fox jumped over the lazy dog'),
(2, 'the dog days of summer')
DECLARE #changesTable TABLE (old varchar(max), new varchar(max) )
INSERT #changesTable VALUES
('dog','cat'),
('the','a')
SELECT
id,
LTRIM((
SELECT ' '+ISNULL(new,word)
FROM #combined c2
CROSS APPLY (SELECT CAST('<a>'+REPLACE(full_santance,' ','</a><a>')+'</a>' AS xml) xml1 ) t1
CROSS APPLY (SELECT n.value('.','varchar(max)') AS word FROM xml1.nodes('a') x(n) ) t2
LEFT JOIN #changesTable ON word = old
WHERE c1.id = c2.id
FOR XML PATH('')
))
FROM #combined c1

Conversion failed in cursor in stored procedure

Input like 111111 and 101,102,103,104
I want to check whether user has access on this requests or not...
I tried a cursor as shown, but I get this error:
Conversion failed when converting the varchar value '101,102,103,104' to data type int.
Code:
ALTER PROCEDURE [dbo].[ValidateRqstId]
#UserID VARCHAR(50),
#RsqtIDs VARCHAR(300)
AS
BEGIN
Declare #RqstId int
Declare #Result int
Declare #UserIDToCheck VARCHAR(50)
Declare #RqstUserVal cursor for
Select RequestId
from REQUEST_LIST
where RequestId in (#RsqtIDs)
BEGIN
OPEN RqstUserVal
FETCH NEXT from RqstUserVal into #RqstId
WHILE(##fetch_status <> -1)
BEGIN
SET #UserIDToCheck = (
select UserId from dbo.REQUEST_LIST where RequestId = #RqstId)
Print(#UserIDToCheck)
If(#UserIDToCheck != #UserID)
SET #Result = 99 ;
--Fetch the next row from the cursor
FETCH RqstUserVal into #RqstId
END
END
CLOSE RqstUserVal
Deallocate RqstUserVal
RETURN #Result
END
Thanks in advance
Depending on your SQL-Server Verion you can use a Table-Valued Function like in this short example
Select * from dbo.test1
Where ID in(
Select * from dbo.F_SplitAsIntTable('1,123,234,456'))
Function defined as
CREATE FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
GO

How i can use cursor to delete record from table

I want to use cursor to delete record from table. How can I do it?
I use MSSQL 2008 Express this code does not delete anything from #temp. I also tried where current of cursor_name did not work.
Here is my sample code:
use AdventureWorks
drop table #temp
select * into #temp from HumanResources.Employee;
declare #eid as int;
declare #nid as varchar(15);
DECLARE Employee_Cursor CURSOR FOR
SELECT A.EmployeeID, A.NationalIDNumber FROM #temp AS A
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#eid > 10)
BEGIN
delete from #temp where #temp.EmployeeID = #eid;
END
FETCH NEXT FROM Employee_Cursor;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
select * from #temp
thanks in advance
use AdventureWorks
select * into #temp from HumanResources.Employee;
declare #eid as int;
declare #nid as varchar(15);
DECLARE Employee_Cursor CURSOR FOR
SELECT A.EmployeeID, A.NationalIDNumber FROM #temp AS A
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#eid > 10)
BEGIN
delete from #temp where current of Employee_Cursor
END
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
select * from #temp
drop table #temp
this works for me
There is a much simpler answer - use this command:
delete from HumanResources.Employee where current of Employee_Cursor
It's called 'Positioned delete' and described at MSDN.
Could you please try in below ways, thanks for your time.
You have fetched data from cursor but didn't push into your variables missed in WHILE loop, please have a look on below code, thanks.
drop table #temp
select * into #temp from Employee;
declare #eid as int;
declare #nid as varchar(15);
DECLARE Employee_Cursor CURSOR FOR
SELECT A.EmployeeID, A.NationalIDNumber FROM #temp AS A
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#eid > 10)
BEGIN
delete from #temp where #temp.EmployeeID = #eid;
END
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
select * from #temp
Update:
I've changes in 2nd FETCH statement, just have added below highlighted part, thanks
FETCH NEXT FROM Employee_Cursor INTO #eid , #nid ;

Resources