In T-SQL I want to loop through a table in a stored procedure, by reading a row by row.
DECLARE #IMAX INT,
#ICOUNT INT,
#INTERFACE_ID_36 INT,
#INTERFACE_ID_38 INT
SELECT * FROM INTERFACE_36_DATA
SET #IMAX = ##ROWCOUNT
SET #ICOUNT = 1
WHILE(#ICOUNT <= #IMAX)
BEGIN
SELECT #INTERFACE_ID_36 = Interface_ID
FROM INTERFACE_36_DATA
WHERE ROW_NUMBER() OVER (ORDER BY id) AS = #ICOUNT --syntax error here
IF #INTERFACE_ID_36 = 10
SET #INTERFACE_ID_38 = 0
ELSE IF #INTERFACE_ID_36 =
I suggest to rewrite it using Cursors as follows faster:
DECLARE #IMAX INT,
#ICOUNT INT,
#INTERFACE_ID_36 INT,
#INTERFACE_ID_38 INT
DECLARE db_cursor CURSOR FOR
SELECT Interface_ID FROM INTERFACE_36_DATA
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #INTERFACE_ID_36
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #INTERFACE_ID_36
-- All your other selects
FETCH NEXT FROM db_cursor INTO #INTERFACE_ID_36
END
CLOSE db_cursor
DEALLOCATE db_cursor
Change your query like below.
As we can't use row_number in where condition.
Check the link here for more descr
WITH cte
AS ( SELECT Interface_ID ,
ROW_NUMBER() OVER ( ORDER BY id ) AS RN
FROM INTERFACE_36_DATA
)
SELECT #INTERFACE_ID_36 = Interface_ID
FROM cte
WHERE RN = #ICOUNT
You can try doing this :
DECLARE #IMAX INT,
#ICOUNT INT,
#INTERFACE_ID_36 INT,
#INTERFACE_ID_38 INT
select *,ROW_NUMBER() OVER (ORDER BY Interface_ID) RowId into #tmptbl from INTERFACE_36_DATA
Declare #I BIGINT = 1
declare #count BIGINT = (select Count(*) from #tmptbl)
while(#I < = #count)
begin
SELECT #INTERFACE_ID_36 = Interface_ID
FROM #tmptbl
where RowId = #I
--- do yor additional stuff
set #I =#I +1
end
Related
My stored procedure is too slow. Can I make it faster?
ALTER PROCEDURE [dbo].[CalBuyAvg]
#companyCode INT
AS
BEGIN
BEGIN TRANSACTION CalBuyAvg
DELETE FROM dbo.tmpCalSood
WHERE CompanyCode = #companyCode
DECLARE #id INT
DECLARE #previd INT
DECLARE #prvBuyAvg DECIMAL(18,0)
DECLARE #prvKcode INT
DECLARE db_cursor CURSOR FOR
SELECT Id, LAG(Id) OVER (ORDER BY kalaCode, Tarikh, id) prevId
FROM dbo.CalMandeVW
ORDER BY kalaCode, Tarikh, id
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #id, #previd
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #prvKcode = KalaCode, #prvBuyAvg = buyAvg
FROM dbo.TmpCalSood
WHERE id = #previd
INSERT INTO dbo.TmpCalSood ([Id], [Tarikh], [KalaCode], [kalaType], [Varede], [fiVarede], [SumVarede],
SaleMali, AnbarCode, pType, pCode, ProjectCode, CompanyCode,
Sadere, fiSadere, SumSadere, Mandeh, LastMande , buyavg)
SELECT
[Id], [tarikh], [KalaCode], 0, [Varede], [fiVarede], [sumVarede],
SaleMali, AnbarCode, pType, pCode, ProjectCode, CompanyCode,
Sadere, fiSadere, SumSadere, Mandeh, lastMande,
CASE
WHEN lastMande = 0
THEN dbo.CalMandeVW.fiVarede
ELSE
CASE
WHEN varede = 0
THEN
CASE
WHEN #prvKcode = kalaCode
THEN #prvBuyAvg
ELSE 0
END
ELSE
CASE
WHEN #prvKcode = kalaCode
THEN ((sumVarede) + (LastMande*#prvBuyAvg)) / (varede+lastMande)
ELSE dbo.CalMandeVW.fiVarede
END
END
END AS newbuyAvg
FROM
dbo.CalMandeVW
WHERE
id = #id
FETCH NEXT FROM db_cursor INTO #id, #previd
END
CLOSE db_cursor
DEALLOCATE db_cursor;
COMMIT
RETURN 1
END
Cursors are performance killers.
Use
INSERT...INTO....SELECT
Syntax for better performance
I am trying to select the second row in a column, I want to continue the loop if the next row is null. Here is my script:
DECLARE #MAXID INT, #Counter INT, #clientId AS int
SET #COUNTER = 2
SET #clientId = 11
SELECT #MAXID = COUNT(DISTINCT vw_masterView.LastVisitDate) FROM vw_MasterView where clientId = #clientId;
WHILE (#COUNTER <= #MAXID)
BEGIN
SELECT myData FROM
(
SELECT myData , ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView where clientId = #clientId
) results
WHERE results.Rownumber = #COUNTER
IF Results.myData IS NOT NULL BREAK;
SET #COUNTER = #COUNTER + 1
END
I get the following error:
The multi-part identifier "Results.myData" could not be bound.
Thank you
There is no variable called Results in there. Set myData at variable and check null with the variable. Try this:
BEGIN
DECLARE #myData VARCHAR(20)
SELECT #myData = myData FROM
(
SELECT
myData,
ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView where clientId = #clientId
) results
WHERE results.Rownumber = #COUNTER
IF #myData IS NOT NULL BREAK;
SET #COUNTER = #COUNTER + 1
END
Your use of the Results.myData is deeply flawed. You are not inside the query itself, so you can't reference a table and column. I believe this will accomplish what you are trying to do though.
DECLARE #clientId AS int = 11
SELECT
*
FROM (
SELECT myData , ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView
where clientId = #clientId
and myData is not null
) a
WHERE Rownumber=2
Expected result is
code descd slnum
======================
10 a 1
11 b 2
12 c 3
I have tried like this but I'm missing somewhere
DECLARE #DF TABLE(code int, descd varchar(7), slnum int)
INSERT INTO #DF VALUES(10, 'a', 0), (11, 'b', 0), (12, 'c', 0)
DECLARE #s as INT = 0
DECLARE #code as INT = 0
DECLARE scursor CURSOR FOR
SELECT code, slnum
FROM #DF
FOR UPDATE of slnum
OPEN scursor
FETCH NEXT FROM scursor INTO #code, #s
WHILE(##FETCH_STATUS = 0)
BEGIN
UPDATE #DF
SET #s = slnum = #s + 1
WHERE current of scursor
FETCH NEXT FROM scursor INTO #code, #s
END
CLOSE scursor
DEALLOCATE scursor
One simple, set-based statement will do - no messy and clunky cursor needed!!
Try this:
WITH CTE AS
(
SELECT
code,
SeqNum = ROW_NUMBER() OVER (ORDER BY code)
FROM
#DF
)
UPDATE d
SET slnum = SeqNum
FROM #DF d
INNER JOIN CTE ON d.Code = CTE.code
Now when you look at your table:
SELECT * FROM #DF
you should get this output:
which is what you were trying to get - correct?
You can without any order as the below:
DECLARE #SeqNum INT = 0
UPDATE #DF
SET slnum = #SeqNum,
#SeqNum += 1
Result:
code descd slnum
----------- ------- -----------
10 a 1
11 b 2
12 c 3
DECLARE #ttb table (code VARCHAR(5), descd VARCHAR(5), slnum int)
INSERT INTO #ttb VALUES(10,'a',0),(11,'b',0),(12,'c',0)
DECLARE #s INT=1
DECLARE #value INT
DECLARE scursor CURSOR FOR
SELECT slnum
FROM #ttb
for update of slnum
OPEN scursor
FETCH NEXT FROM scursor
INTO #value
WHILE ##FETCH_STATUS = 0
BEGIN
update #ttb
set slnum=#s
where current of scursor
select #s=#s+1
FETCH NEXT FROM scursor INTO #value
END
CLOSE scursor
DEALLOCATE scursor
select * from #ttb
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...
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