SQL Server 2000 - Breaking out of a loop - sql

I am not good at SQL Server 2000. I have a comma-delimited list of ids. I need to see if that ID exists in a table. If it does, I want to break out of the loop with that ID saved in a variable that I can use in my stored procedure. This is what I am trying right now:
DECLARE #coreID INT
SET #coreID=NULL
DECLARE #itemID NVARCHAR(50)
DECLARE itemCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT [String] AS 'itemID' FROM dbo.SplitListIntoTable(#myIDs)
OPEN itemCursor
FETCH NEXT FROM itemCursor INTO #itemID
WHILE ##FETCH_STATUS = 0 BEGIN
-- If #itemID EXISTS IN MyTable set #coreID=#itemID and Break. How do I do this?
FETCH NEXT FROM itemCursor INTO #itemID
END
CLOSE itemCursor
DEALLOCATE itemCursor
Thank you!

Ideally, you shouldn't use a cursor as performance won't be great. If you can do it as a set-based statement, do that instead, maybe like this:
SELECT TOP 1 #CoreID = [String]
FROM dbo.SplitListIntoTable(#myIDs) x
JOIN MyTable t ON x.[String] = t.ID
However, if you have a real reason to use a cursor, you can use the BREAK statement to break out of a WHILE loop
e.g.
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM MyTable WHERE Id = #ItemID)
BEGIN
SET #CoreId = #ItemId
BREAK
END
FETCH NEXT FROM itemCursor INTO #itemID
END

I don't know how to do this using a cursor, but I supect you can do this much better (faster) with a a join. If the output of dbo.SplitListIntoTable(#myIDs) is actually an odered table, then you can output a table with another column what is say the string numer, 1, 2, 3, etc...
(I don't have sql in front of me to test this but something like)
create table t(itemNum int identity, itemId nvarchar(max))
insert into t (item id) select 1 from dbo.SplitListIntoTable(#myIDs)
Then join the two and take the top one
set #coreID =
select top 1 #itemID
from MyTable m
inner join t t.itemid = m.itemid
order by m.itemNum asc
of course you could use a CTE, table var or temp table too.

Related

SQL combine WITH clause and Cursor

How is it possible to use declared parameter, a WITH clause and a cursor in one Query
declare #TOP10 table (Cat Nvarchar(max),SubGUID uniqueidentifier)
declare #Sub uniqueidentifier
declare GUID_Cursor cursor FOR
(select SubGUID from dbo.Sub with(nolock) where year=2016)
;
with [MyTable] as
(
Select SubGUID, color from dbo.Cars with(nolock) where color ='blue'
)
open GUID_Cursor
fetch next from GUID_Cursor into #Sub
while ##FETCH_STATUS=0
begin
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID from [MyTable]
where SubGUID=#Sub
fetch next from GUID_Cursor into #Sub
end
close GUID_Cursor
deallocate GUID_Cursor
select * from #TOP10
I just cant find a way tu open the cursor after using the WITH clause.
Can anyone help me?
Well, aside from the logic of your code (for me - there is no need of cursor here at all), you should just move CTE declaration closer to the place where you're using it.
;with [MyTable] as
(
Select SubGUID, color from dbo.Cars with(nolock) where color ='blue'
)
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID from [MyTable]
where SubGUID=#Sub
But really, all your code could be replaced with:
declare #TOP10 table (Cat Nvarchar(max),SubGUID uniqueidentifier)
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID
from dbo.Cars with(nolock)
where
color ='blue'
and SubGUID in (select SubGUID from dbo.Sub with(nolock) where year=2016)
select * from #TOP10
I agree with #AndyKorneyev, but for the sake of experiment:
declare #crTest CURSOR,
#name VARCHAR(1000),
#type VARCHAR(1000)
set #crTest = CURSOR FAST_FORWARD FOR
WITH cteObjs AS (
SELECT TOP 10 NAME, type
FROM sys.objects o
)
select name, type
from cteObjs
OPEN #crTest
FETCH NEXT FROM #crTest
INTO #name, #type
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #name
PRINT #type
FETCH NEXT FROM #crTest
INTO #name, #type
END
CLOSE #crTest
DEALLOCATE #crTest
Don't want to repeat above snippets but be aware that WITH clause result set must be processed directly at it's end.
Unlike your #tableVar, the scope of the result set that is returned by cte is not valid within complete batch and therefore must be followed by SELECT, INSERT, UPDATE or DELETE statement.
(for details and examples see: https://msdn.microsoft.com/en-us/library/ms175972.aspx or follow one of the other answers).
e. g.
--> declare var:
declare #tableVar table (colOne int, colTwo nvarchar(30)
);
--> use cte:
with preOne as(select carId, color from pcUser with (nolock)
)
--> directly followed by insert:
insert into #tableVar(userId, logInName)
select colOne, colTwo from #tableVar;
--> use var in cursor or anywhere else in the batch
Though I'm not exactly sure what you aim at, perhaps a short join could do the trick here:
select a.[SubGUID]
from [dbo].[Sub] as a with (nolock)
inner join [dbo].[Cars] as b with (nolock) on a.[SubGUID] = b.[SubGUID]
where a.[year] = 2016 and b.[color] = 'blue';
In the end I used temp tabeles like #TOP10. Which I droped after the query. As soon as I got rid of the with [MyTable] as part I had no more problems with the parameters.
Thank you all lot.

Batch-wise fetch records from cursor

DECLARE table_cursor CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT column5
FROM testdb.tableA
OPEN table_cursor;
FETCH NEXT FROM table_cursor INTO #m_column5
WHILE ##FETCH_STATUS = 0
BEGIN
//two update queries are here
END
CLOSE table_cursor;
DEALLOCATE table_cursor;
Let's say this returns 10 million records. Can I fetch the results batch-wise in SQL Server 2014?
Is there a better approach I can go for? (considering the performance fact)
It is very hard to figure out what you are trying to do with that cursor.
Based on whatever little you've shared, it seems that what you really want to do is an UPDATE based on a TOP clause:
UPDATE TOP (10) Sales.Store
SET SalesPersonID = 276
WHERE SalesPersonID = 275;
or an UPDATE FROM SELECT if you need a join:
UPDATE [dbo].[Events]
SET [datetime]=SYSDATETIME()
FROM (SELECT TOP 1000 [id] FROM [dbo].[Events] WHERE [datetime]<CONVERT(DATE,SYSDATETIME()) [e2]
INNER JOIN [dbo].[Events] ON [Events].[id] = [e2].[id];
If you find that you really do need a cursor to support your process, you can probably then go with:
WHILE EXISTS (SELECT 1 FROM [dbo].[Events] WHERE [datetime]<CONVERT(DATE,SYSDATETIME())
BEGIN
-- Your logic here
END;

How do I use a simple query to search a list of data from a table and return non-duplicate results in a table?

I am trying to loop through the data in a table, and using the data to search to return the results in another table.
How do I prevent duplicates from adding to the table? Note that the order of the query results adding are very important. So if the results are already added, I don't want them to be added again. Note that the original ranking done by the full search category is misleading, I don't want to use that.
I am using cursor, but I was told it can be solved using simple query; how do I do that?
Below is the code.
...
DECLARE #subQ NVARCHAR(200)
SET #subQ = ''
DECLARE cur1 CURSOR FOR
SELECT combination FROM #Subqueries
OPEN cur1
FETCH NEXT FROM cur1 INTO #subQ
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #Results (app_id, rank, importance)
SELECT app_id, rank, 1
FROM CONTAINSTABLE(dbo.Applications, display_name, #subQ) KEY_TBL
INNER JOIN Applications App
ON KEY_TBL.[KEY] = App.app_id
FETCH NEXT FROM cur1 INTO #subQ
END
CLOSE cur1
DEALLOCATE cur1
...
You used 1 = 1 in while loop. so it will be showing always true condition and resultant is that loop converted to infinite loop.
Change your condition in while loop.
try this while loop
DECLARE #subQ NVARCHAR(200)
DECLARE #mn int
DECLARE #mx int
DECLARE #val varchar(100)
SET #subQ = ''
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as rn,* from #Subqueries)
select #mn=MIN(rn),#mx=MAX(rn) from CTE
WHILE #mn>=#mx
BEGIN
select #val=somecolm from CTE where rn=#mn
--do here for each value of any column in CTE
SET #mn=#mn+1
END

SQL while loop with Temp Table

I need to create a temporary table and then update the original table. Creating the temporary table is not a problem.
create table #mod_contact
(
id INT IDENTITY NOT NULL PRIMARY KEY,
SiteID INT,
Contact1 varchar(25)
)
INSERT INTO #mod_contact (SiteID, Contact1)
select r.id, r.Contact from dbo.table1 r where CID = 142
GO
Now I need to loop through the table and update r.contact = SiteID + r.contact
I have never used a while loop before and can't seem to make any examples I have seen work.
You can do this in multiple ways, but I think you're looking for a way using a cursor.
A cursor is sort of a pointer in a table, which when incremented points to the next record. ( it's more or less analogeous to a for-next loop )
to use a cursor you can do the following:
-- DECLARE the cursor
DECLARE CUR CURSOR FAST_FORWARD READ_ONLY FOR SELECT id, siteId, contract FROM #mod_contract
-- DECLARE some variables to store the values in
DECLARE #varId int
DECLARE #varSiteId int
DECLARE #varContract varchar(25)
-- Use the cursor
OPEN CUR
FETCH NEXT FROM CUR INTO #varId, #varSiteId, #varContract
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE dbo.table1
SET contract = #varSiteId + #varContract -- It might not work due to the different types
WHERE id = #varId
FETCH NEXT FROM CUR INTO #varId, #varSiteId, #varContract
END
CLOSE CUR
DEALLOCATE CUR
It's not the most efficient way to get this done, but I think this is what you where looking for.
Hope it helps.
Use a set based approach - no need to loop (from the little details):
UPDATE
r
SET
r.Contact = m.SiteID + r.Contact
FROM
table1 r
INNER JOIN
#mod_contact m
ON m.id=r.id
Your brain wants to do this:
while records
update(i); //update record i
records = records + 1
end while
SQL is set based and allows you to take a whole bunch of records and update them in a single command. The beauty of this is you can use the WHERE clause to filter certain rows that are not needed.
As others have mentioned, learning how to do loops in SQL is generally a bad idea; however, since you're trying to understand how to do something, here's an example:
DECLARE #id int
SELECT #ID =1
WHILE #ID <= (SELECT MAX(ID) FROM table_1)
-- while some condition is true, then do the following
--actions between the BEGIN and END
BEGIN
UPDATE table_1
SET contact = CAST(siteID as varchar(100)) + contact
WHERE table_1.CID = #ID
--increment the step variable so that the condition will eventually be false
SET #ID = #ID + 1
END
--do something else once the condition is satisfied
PRINT 'DONE!! Don't try this in production code...'
Try this one:
-- DECLARE the cursor
DECLARE CUR CURSOR FAST_FORWARD READ_ONLY FOR SELECT column1,column2 FROM table
-- DECLARE some variables to store the values in
DECLARE #varId int
DECLARE #varSiteId int
--DECLARE #varContract varchar(25)
-- Use the cursor
OPEN CUR
FETCH NEXT FROM CUR INTO #varId, #varSiteId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT *
FROM Table2
WHERE column1 = #varId
AND column2 = #varSiteId
FETCH NEXT FROM CUR INTO #varId, #varSiteId
END
CLOSE CUR
DEALLOCATE CUR
need to create a temporary table and then up date the original table.
Why use a temporary table at all? Your CID column doesn't appear in the temporary table, so I don't see how you can successfully update the original table using SiteID, unless there is only one row where CID = 142 in which using a temp table is definitely overkill.
You can just do this:
UPDATE dbo.table1
SET contact = SiteID + contact
WHERE CID = 142;
Here's a related example which may help getting you to 'think in SQL':
UPDATE T
SET A = B, B = A;
Assuming A and B are of the same type, this would successfully swap their values.

How to update a column fetched by a cursor in TSQL

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