I am trying to call a stored proc in a loop for each record in a query.
Here's what I have now:
WITH X AS (
SELECT customerid FROM DBcustomers
)
WHILE(EXISTS (SELECT * FROM X))
BEGIN
SELECT TOP 1 #Id = customerid FROM X
EXEC [dbo].[AnotherSP] #Id , 1
DELETE FROM X WHERE customerid = #Id
END
, but this does not compile.
I guess you want to do UPDATE like this:
UPDATE ProductTable SET IsActive = 1
WHERE customerid IN (SELECT customerid FROM DBcustomers)
or for your edited question use cursors:
DECLARE #id BIG
DECLARE Customers CURSOR
FOR SELECT customerid FROM DBcustomers
OPEN Customers FETCH NEXT FROM Customers INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [dbo].[AnotherSP] #id , 1
FETCH NEXT FROM Customers INTO #id
END
CLOSE Customers
DEALLOCATE Customers
UPDATE productTable
SET isActive = 1
WHERE customerId IN
(
SELECT customerId
FROM dbCustomers
)
Update:
DECLARE cr_customer CURSOR
FOR
SELECT customerId
FROM dbCustomers
DECLARE #id INT
FETCH NEXT
FROM cr_customer
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC anotherSP #id , 1
FETCH NEXT
FROM cr_customer
INTO #id
END
CLOSE cr_customer
DEALLOCATE
cr_customer
Something like this might do it:
DECLARE #Id INT;
SELECT TOP 1 #Id = customerid FROM DBcustomers ORDER BY customerid;
WHILE #Id IS NOT NULL
BEGIN
EXEC [dbo].[AnotherSP] #Id, 1;
DECLARE #LastId INT;
SELECT #LastId = #Id;
SELECT #Id = NULL;
SELECT #Id = customerid FROM DBcustomers WHERE customerId > #LastId ORDER BY customerid;
END;
Related
Consider the following database table. It consists of 3 columns: Id, ParentId, Enabled.
I would like to produce a result set similar to the following. Basically for each record that has a Parent ID, I want to to display an additional column Enabled Parent Id. This column basically needs to recursively checks the hierarchy of the key, and stops when a key that is Enabled = True is found.
I would like this to achieve this on the fly, without requiring to add any additional computed columns in the table.
Maybe this could be achieved using a CTE.
Try this CTE query:
WITH T1 as
(SELECT id,
parentId,
NULL as EnabledParentId,
ParentID as NextParent
FROM T
WHERE ParentID is not null
UNION ALL
SELECT T1.id,
T1.parentId,
CASE WHEN T.enabled = 1
THEN T.ID
ELSE NULL END
as EnabledParentId,
T.ParentID as NextParent
FROM T1
JOIN T ON T1.NextParent = T.Id
WHERE (nextParent is not Null) and (EnabledParentId IS NULL)
)
SELECT ID,
ParentID,
EnabledParentID
FROM T1
WHERE EnabledParentId IS NOT NULL
OR NextParent IS NULL
ORDER BY ID;
DECLARE #myTable TABLE
(
Id INT NOT NULL,
ParentId INT NOT NULL,
EnabledParentId INT
)
DECLARE myCursor CURSOR FOR
SELECT Id
FROM T
WHERE ParentId IS NOT NULL
ORDER BY Id;
OPEN myCursor;
DECLARE #currentId INT;
FETCH NEXT FROM myCursor INTO #currentId;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Exists BIT = 0;
DECLARE #ParentId INT
SELECT #ParentId = ParentId
FROM T
WHERE Id = #currentId
WHILE (#ParentId IS NOT NULL AND #Exists = 0)
BEGIN
IF EXISTS(SELECT * FROM T WHERE Id = #ParentId AND IsEnabled = 1)
BEGIN
SET #Exists = 1
END
ELSE
BEGIN
SELECT #ParentId = ParentId
FROM T
WHERE Id = #ParentId
END
IF (#Exists = 1 OR #ParentId IS NULL)
BEGIN
INSERT INTO #myTable
SELECT Id, ParentId, #ParentId
FROM T
WHERE Id = #currentId
END
END
FETCH NEXT FROM myCursor INTO #currentId;
END
CLOSE myCursor;
DEALLOCATE myCursor;
SELECT *
FROM #myTable
ORDER BY 1
I'm not allowed to use cursor, or temp tables. I have to use a while loop only.
Table1 - Readonly Table (non editable)
id name
M01 Raja
M02 Ravi
M03 Vijay
M04 suresh
Query
Declare #TotRecord int, #CurrRecord Int, #id varchar(10)
Select #TotRec = COUNT(*) from Table1
Set #CurrRec = 1
WHILE (#CurrRec <=#TotRec)
BEGIN
--*Here i want to get the id from table,
--next time i need to get second id.
--next time i need to get third id.
--....
--1st time i can get the top 1 id by using this below query
Select top 1 #id = id from table
--Next time i want to get second id.*
SET #CurrRec = #CurrRec + 1
END
Use the pattern of
SELECT TOP 1 * FROM table WHERE id > #iterator ORDER BY id ASC
in a loop
DECLARE #TotRecord INT;
DECLARE #CurrRecord INT;
DECLARE #id VARCHAR(10);
SELECT #TotRecord = COUNT(*) from Table1;
SELECT #CurrRecord = 1;
SELECT TOP 1 #id = id FROM Table1 ORDER BY id;
WHILE (#CurrRecord <= #TotRecord)
BEGIN
--Do something...
--Get the next id
SELECT TOP 1 #id = id FROM Table1 WHERE id > #id ORDER BY id;
SELECT #CurrRecord = #CurrRecord + 1;
END;
But note that this will only work if the id is unique in Table1.
Declare #TotRecord int, #CurrRecord Int, #id varchar(10)
Select #TotRec = COUNT(*) from Table1
Set #CurrRec = 1
WHILE (#CurrRec <=#TotRec)
BEGIN
select LAST(id) from (SELECT TOP #CurrRec id
FROM Table1);
//Here you will get the last record, i havent tried this, but iam sure about the logic , hope this one helps
SET #CurrRec = #CurrRec + 1
END
If ID is sequence number (M01, M02, M03,...), then you can use the following with in while loop
SELECT TOP 1 #id = id FROM table WHERE CAST(REPLACE(id,'M','') AS INT) = #CurrRecord
Full Query
DECLARE #TotRecord INT, #CurrRecord INT, #id VARCHAR(10)
SELECT #TotRecord = COUNT(*) FROM Table1
SET #CurrRecord = 1
WHILE #CurrRecord <= #TotRecord
BEGIN
-- Get ID
SELECT TOP 1 #id = id FROM Table1 WHERE CAST(REPLACE(id,'M','') AS INT) = #CurrRecord
-- Do something
SET #CurrRecord = #CurrRecord + 1
END
You can use cursor for get records of your query one to one.
For example :
Declare #Col1 INT,
#Col2 INT,
...
DECLARE X CURSOR FOR
SELECT Col1, col2 , ...
FROM YourTableName
OPEN X
FETCH NEXT FROM X INTO #Col1, #Col2, ...
WHILE ##FETCH_STATUS=0 BEGIN
--your code to do with columns data of your record
FETCH NEXT FROM X INTO #Col1, #Col2, ...
END
CLOSE X
DEALLOCATE X
You can also use where for your idea:
DECLARE #Id INT,
#OldId INT,
#Col1 INT,
#Col2 INT
...
WHILE 1=1 BEGIN
SET #OldId = #Id
SET #Id = NULL
SELECT TOP 1 #Id = Id,
#Col1 = Column1,
#Col2 = Column2
FROM YourTable
WHERE (#OldId IS NULL OR Id > #OldId) ORDER BY Id
IF #Id IS NULL BREAK
-- your Code Here
END
Select id,name,row_number() over (order by id) 'Row' into #temp
from table
Select #id = id from #temp where row = #current
Use temp table to get the next id
How do I loop through a set of records from a select statement?
Say I have a few records that I wish to loop through and do something with each record. Here's a primitive version of my select statement:
select top 1000 * from dbo.table
where StatusID = 7
By using T-SQL and cursors like this :
DECLARE #MyCursor CURSOR;
DECLARE #MyField YourFieldDataType;
BEGIN
SET #MyCursor = CURSOR FOR
select top 1000 YourField from dbo.table
where StatusID = 7
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyField
WHILE ##FETCH_STATUS = 0
BEGIN
/*
YOUR ALGORITHM GOES HERE
*/
FETCH NEXT FROM #MyCursor
INTO #MyField
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;
This is what I've been doing if you need to do something iterative... but it would be wise to look for set operations first. Also, do not do this because you don't want to learn cursors.
select top 1000 TableID
into #ControlTable
from dbo.table
where StatusID = 7
declare #TableID int
while exists (select * from #ControlTable)
begin
select top 1 #TableID = TableID
from #ControlTable
order by TableID asc
-- Do something with your TableID
delete #ControlTable
where TableID = #TableID
end
drop table #ControlTable
Small change to sam yi's answer (for better readability):
select top 1000 TableID
into #ControlTable
from dbo.table
where StatusID = 7
declare #TableID int
while exists (select * from #ControlTable)
begin
select #TableID = (select top 1 TableID
from #ControlTable
order by TableID asc)
-- Do something with your TableID
delete #ControlTable
where TableID = #TableID
end
drop table #ControlTable
By using cursor you can easily iterate through records individually and print records separately or as a single message including all the records.
DECLARE #CustomerID as INT;
declare #msg varchar(max)
DECLARE #BusinessCursor as CURSOR;
SET #BusinessCursor = CURSOR FOR
SELECT CustomerID FROM Customer WHERE CustomerID IN ('3908745','3911122','3911128','3911421')
OPEN #BusinessCursor;
FETCH NEXT FROM #BusinessCursor INTO #CustomerID;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #msg = '{
"CustomerID": "'+CONVERT(varchar(10), #CustomerID)+'",
"Customer": {
"LastName": "LastName-'+CONVERT(varchar(10), #CustomerID) +'",
"FirstName": "FirstName-'+CONVERT(varchar(10), #CustomerID)+'",
}
}|'
print #msg
FETCH NEXT FROM #BusinessCursor INTO #CustomerID;
END
Just another approach if you are fine using temp tables.I have personally tested this and it will not cause any exception (even if temp table does not have any data.)
CREATE TABLE #TempTable
(
ROWID int identity(1,1) primary key,
HIERARCHY_ID_TO_UPDATE int,
)
--create some testing data
--INSERT INTO #TempTable VALUES(1)
--INSERT INTO #TempTable VALUES(2)
--INSERT INTO #TempTable VALUES(4)
--INSERT INTO #TempTable VALUES(6)
--INSERT INTO #TempTable VALUES(8)
DECLARE #MAXID INT, #Counter INT
SET #COUNTER = 1
SELECT #MAXID = COUNT(*) FROM #TempTable
WHILE (#COUNTER <= #MAXID)
BEGIN
--DO THE PROCESSING HERE
SELECT #HIERARCHY_ID_TO_UPDATE = PT.HIERARCHY_ID_TO_UPDATE
FROM #TempTable AS PT
WHERE ROWID = #COUNTER
SET #COUNTER = #COUNTER + 1
END
IF (OBJECT_ID('tempdb..#TempTable') IS NOT NULL)
BEGIN
DROP TABLE #TempTable
END
You could choose to rank your data and add a ROW_NUMBER and count down to zero while iterate your dataset.
-- Get your dataset and rank your dataset by adding a new row_number
SELECT TOP 1000 A.*, ROW_NUMBER() OVER(ORDER BY A.ID DESC) AS ROW
INTO #TEMPTABLE
FROM DBO.TABLE AS A
WHERE STATUSID = 7;
--Find the highest number to start with
DECLARE #COUNTER INT = (SELECT MAX(ROW) FROM #TEMPTABLE);
DECLARE #ROW INT;
-- Loop true your data until you hit 0
WHILE (#COUNTER != 0)
BEGIN
SELECT #ROW = ROW
FROM #TEMPTABLE
WHERE ROW = #COUNTER
ORDER BY ROW DESC
--DO SOMTHING COOL
-- SET your counter to -1
SET #COUNTER = #ROW -1
END
DROP TABLE #TEMPTABLE
this way we can iterate into table data.
DECLARE #_MinJobID INT
DECLARE #_MaxJobID INT
CREATE TABLE #Temp (JobID INT)
INSERT INTO #Temp SELECT * FROM DBO.STRINGTOTABLE(#JobID,',')
SELECT #_MinJID = MIN(JobID),#_MaxJID = MAX(JobID) FROM #Temp
WHILE #_MinJID <= #_MaxJID
BEGIN
INSERT INTO Mytable
(
JobID,
)
VALUES
(
#_MinJobID,
)
SET #_MinJID = #_MinJID + 1;
END
DROP TABLE #Temp
STRINGTOTABLE is user define function which will parse comma separated data and return table. thanks
I think this is the easy way example to iterate item.
declare #cateid int
select CateID into [#TempTable] from Category where GroupID = 'STOCKLIST'
while (select count(*) from #TempTable) > 0
begin
select top 1 #cateid = CateID from #TempTable
print(#cateid)
--DO SOMETHING HERE
delete #TempTable where CateID = #cateid
end
drop table #TempTable
Please see the code below:
DECLARE #ID int
DECLARE #errorflag int
DECLARE Warning_Cursor CURSOR FOR
SELECT TOP 3 ID FROM Warnings
SET #errorflag = #errorflag + ##Error
OPEN Warning_cursor
SET #errorflag = #errorflag + ##Error
FETCH NEXT FROM Warning_cursor INTO #ID
WHILE ##FETCH_STATUS = 0
begin
SELECT #ID
FETCH NEXT FROM Warning_cursor INTO #ID
END
CLOSE Warning_cursor
DEALLOCATE Warning_cursor
The cursor returns three tables with one row each. How can I return one table with three rows?
Why don't you just do,
SELECT TOP 3 ID FROM Warnings
More generally, if you are using a cursor, you are probably doing it wrong.
If you really have to use a cursor for some reason that is not part of the question. You could do
DECLARE #Id int;
DECLARE #Ids TABLE (Id Int);
DECLARE Warning_Cursor CURSOR FOR SELECT TOP 3 ID FROM Warnings;
OPEN Warning_cursor;
FETCH NEXT FROM Warning_cursor INTO #Id;
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT #Ids SELECT #Id;
FETCH NEXT FROM Warning_cursor INTO #Id;
END
CLOSE Warning_cursor;
DEALLOCATE Warning_cursor;
SELECT Id FROM #Ids;
The answer was to create a temporary table as follows:
DECLARE #ID int
DECLARE #errorflag int
DECLARE #CONCATRESULT TABLE (ID INT)
DECLARE Warning_Cursor CURSOR FOR
SELECT TOP 3 ID FROM Warnings
SET #errorflag = #errorflag + ##Error
OPEN Warning_cursor
SET #errorflag = #errorflag + ##Error
FETCH NEXT FROM Warning_cursor INTO #ID
WHILE ##FETCH_STATUS = 0
begin
INSERT into #CONCATRESULT (ID) VALUES (#ID)
FETCH NEXT FROM Warning_cursor INTO #ID
END
CLOSE Warning_cursor
DEALLOCATE Warning_cursor
select id from #CONCATRESULT
I have a stored proc that inserts new records in my 2008 SQL server table via xml input:
CREATE PROCEDURE ins_AddBinsToBox
#BoxId BIGINT,
#BinIds XML
AS
BEGIN
INSERT INTO WebServiceBoxDetails
(
BinId,
BoxId
)
SELECT
ParamValues.ID.value('.','VARCHAR(20)'),
#BoxId
FROM
#binIds.nodes('/Bins/id') AS ParamValues(ID)
This works great for inserting new rows, the thing i'm confused about is updating (via UPDATE statement) this same table with new xml input?
Table:
Id(PK) BoxNumber BinId
(bigint) (bigint) (int)
_______________________
1 12 334
2 12 445
3 12 776
4 16 223
5 16 669
Command to be used:
EXEC upd_Box #binIds='<Bins><id>7848</id><id>76554</id><id>71875</id></Bins>', #BoxId=12
Sorry for the delay. Here is the answer for your second part(this should work for both update and insert)
DECLARE #binIds AS XML = '<Bins><id>44</id><id>55</id><id>66</id><id>77</id></Bins>'
DECLARE #id INT
DECLARE #newid INT
DECLARE #count INT = 1
DECLARE #BinIdTable TABLE(RowNumber INT, BinId INT)
DECLARE #BoxNumber INT = 12
DECLARE #RowCount INT = 0
INSERT #BinIdTable(RowNumber, BinId)
SELECT ROW_NUMBER() OVER(ORDER BY ID), ParamValues.ID.value('.','INT')
FROM
#binIds.nodes('/Bins/id') AS ParamValues(ID)
SELECT #RowCount = COUNT(*) FROM #BinIdTable
DECLARE MyCursor CURSOR FOR
SELECT id FROM WebServiceBoxDetails WHERE BoxNumber = #BoxNumber ORDER BY id
OPEN MyCursor
FETCH NEXT FROM MyCursor
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #newid = B.BinId
FROM #BinIdTable B
WHERE RowNumber = #count
UPDATE WebServiceBoxDetails
SET BinId = #newid
WHERE Id = #id
SET #count = #count + 1
FETCH NEXT FROM MyCursor
INTO #id
END
CLOSE MyCursor
DEALLOCATE MyCursor
IF #RowCount >= #count
BEGIN
INSERT INTO WebServiceBoxDetails
SELECT #BoxNumber, BinId
FROM #BinIdTable
WHERE RowNumber >= #count
END
Let me know how it goes.
You can use the NODES method again.
UPDATE WebServiceBoxDetails
SET BinID = ParamValues.ID.value('#BinID','VARCHAR(20)')
FROM
#bindIDs.nodes('/Bins/id') AS ParamValues(ID)
JOIN WebServiceBoxDetails w ON w.ID = ParamValues.ID.value('#id','VARCHAR(20)')
WHERE w.BoxNumber = #BoxID
Try this
DECLARE #binIds AS XML = '<Bins><id>7848</id><id>76554</id><id>71875</id></Bins>'
DECLARE #id INT
DECLARE #newid INT
DECLARE #count INT = 1
DECLARE #BinIdTable TABLE(RowNumber INT, BinId INT)
INSERT #BinIdTable(RowNumber, BinId)
SELECT ROW_NUMBER() OVER(ORDER BY ID), ParamValues.ID.value('.','INT')
FROM
#binIds.nodes('/Bins/id') AS ParamValues(ID)
DECLARE MyCursor CURSOR FOR
SELECT id FROM WebServiceBoxDetails WHERE BoxNumber = 12 ORDER BY id
OPEN MyCursor
FETCH NEXT FROM MyCursor
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #newid = B.BinId
FROM #BinIdTable B
WHERE RowNumber = #count
UPDATE WebServiceBoxDetails
SET BinId = #newid
WHERE Id = #id
SET #count = #count + 1
FETCH NEXT FROM MyCursor
INTO #id
END
CLOSE MyCursor
DEALLOCATE MyCursor
Let me know how it goes.