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
Related
I am trying to get a list back from a stored procedure, but I believe I may have used the wrong method?
The data I am getting back is fine, but is breaking up the results into sections instead of one continuous result.
I need it to be in one continuous result as it needs to be then exported out to an accounting program.
UPDATE
I maybe should have mentioned I am teaching myself SQL and procedures, so I don't entirely know what I am doing, so please forgive me. :)
I spent most of last week writing and re-writing before finally coming here to ask for help.
What I had written was overly complicated I now realise (not to mention not giving the response the way I needed.) I was trying to repurpose code I had found elsewhere.
This is my corrected code.
#varBillingDealerPeriodID int
AS
DECLARE #BillingDealerBatchRosterID int;
BEGIN TRY
SELECT count( * ) AS ItemTotalCount
, di.DealerName
, di.DealerID
, bdbr.BillingDateTo
, bdinr.BillingDealerInvoiceNumber
FROM dbo.billing_dealer_batch_item bdbi
LEFT JOIN dbo.dealer_info di ON di.DealerID = bdbi.DealerID
LEFT JOIN dbo.billing_dealer_batch_roster bdbr ON bdbr.BillingDealerBatchRosterID = bdbi.BillingDealerBatchRosterID
LEFT JOIN dbo.billing_dealer_invoice_number_roster bdinr ON bdinr.DealerID = di.DealerID
WHERE bdbi.BillingDealerBatchRosterID IN (
SELECT DISTINCT BillingDealerBatchRosterID
FROM dbo.billing_dealer_batch_roster
WHERE BillingDealerPeriodID = #varBillingDealerPeriodID
)
AND bdbi.ItemConditionID < 2
GROUP BY di.DealerName
, di.DealerID
, bdbr.BillingDateTo
, bdinr.BillingDealerInvoiceNumber
END TRY
Thank you for everyone's help!
Get rid of the cursor and the loop.
Then change this
AND bdbi.BillingDealerBatchRosterID = #BillingDealerBatchRosterID
To this
AND bdbi.BillingDealerBatchRosterID IN (
SELECT DISTINCT BillingDealerBatchRosterID
FROM dbo.billing_dealer_batch_roster
WHERE BillingDealerPeriodID = #varBillingDealerPeriodID
)
By using the cursor you can just use the following codes. Here, you may need to change the data type for the temp table as per your need.
CREATE PROCEDURE [dbo].[billing_generate_invoice]
#varBillingDealerPeriodID int
AS
BEGIN
IF OBJECT_ID('tempdb..##tmp_billing_generate_invoice', 'U') IS NOT NULL
BEGIN
DROP TABLE ##tmp_billing_generate_invoice
END
CREATE TABLE ##tmp_billing_generate_invoice
(
ItemTotalCount INT,
BillingDealerBatchRosterID INT,
DealerName VARCHAR(500),
ServiceLevelID INT,
FreshItemFeeID INT,
WebsiteFeeID INT,
WebsiteBillingFrequencyID INT,
BillingDateTo DATE,
BillingDealerInvoiceNumber VARCHAR(500)
)
DECLARE #BillingDealerBatchRosterID int;
DECLARE MyCursor CURSOR LOCAL FOR
SELECT BillingDealerBatchRosterID
FROM dbo.billing_dealer_batch_roster
WHERE BillingDealerPeriodID = #varBillingDealerPeriodID;
OPEN MyCursor;
FETCH NEXT FROM MyCursor INTO #BillingDealerBatchRosterID;
WHILE ##fetch_status = 0
BEGIN
-- START
INSERT INTO ##tmp_billing_generate_invoice(
ItemTotalCount,
BillingDealerBatchRosterID,
DealerName,
ServiceLevelID,
FreshItemFeeID,
WebsiteFeeID,
WebsiteBillingFrequencyID,
BillingDateTo,
BillingDealerInvoiceNumber
)
SELECT
COUNT(*) AS ItemTotalCount,
bdbr.BillingDealerBatchRosterID,
di.DealerName,
-- di.DealerID
da.ServiceLevelID,
da.FreshItemFeeID,
da.WebsiteFeeID,
da.WebsiteBillingFrequencyID,
bdbr.BillingDateTo,
bdinr.BillingDealerInvoiceNumber
FROM
dbo.billing_dealer_batch_item bdbi
LEFT JOIN
dbo.dealer_info di ON di.DealerID = bdbi.DealerID
LEFT JOIN
dbo.dealer_account da ON da.DealerID = di.DealerID
LEFT JOIN
dbo.billing_dealer_batch_roster bdbr ON bdbr.BillingDealerBatchRosterID = bdbi.BillingDealerBatchRosterID
INNER JOIN
dbo.billing_dealer_invoice_number_roster bdinr ON bdinr.DealerID = di.DealerID
-- LEFT JOIN dbo.lookup__rate_weekly_fee_LR lrwf ON lrwf.ItemQuantity = ItemTotalCount
-- LEFT OUTER JOIN dbo.billing_dealer_batch bdb ON bdb.DealerID = di.DealerID
WHERE
bdbi.DealerID IN (SELECT DISTINCT DealerID
FROM dbo.billing_dealer_batch
WHERE DealerID = bdbi.DealerID
AND BillingDealerBatchRosterID = #BillingDealerBatchRosterID
AND DealerAccountStatusID = 4 -- Dealer Status 4 is ACTIVE only
)
AND bdbi.BillingDealerBatchRosterID = #BillingDealerBatchRosterID -- Roster Week
AND bdbi.ItemConditionID < 2 -- Less than 2 is AS-IS and Used
GROUP BY
bdinr.BillingDealerInvoiceNumber,
bdbi.DealerID,
di.DealerName,
-- di.DealerID
bdbr.BillingDateTo,
da.ServiceLevelID,
da.FreshItemFeeID,
da.WebsiteFeeID,
da.WebsiteBillingFrequencyID,
bdbr.BillingDealerBatchRosterID
ORDER BY
bdinr.BillingDealerInvoiceNumber;
-- END
FETCH NEXT FROM MyCursor INTO #BillingDealerBatchRosterID;
END;
CLOSE MyCursor;
DEALLOCATE MyCursor;
SELECT * FROM ##tmp_billing_generate_invoice
END
We have DVD Rental company. In this particular scenario we consider only Member, Rental and Membership tables.
The task is to write a trigger that prevents a customer from being shipped a DVD
if they have reached their monthly limit for DVD rentals as per their membership contract using the function.
My trigger leads to infinite loop. It works without While loop, but then it does not work properly, if I consider multiple updates to the Rental table. Where I am wrong?
-- do not run, infinite loop
CREATE OR ALTER TRIGGER trg_Rental_StopDvdShip
ON RENTAL
FOR UPDATE
AS
BEGIN
DECLARE #MemberId INT
DECLARE #RentalId INT
SELECT * INTO #TempTable FROM inserted
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
IF UPDATE(RentalShippedDate)
BEGIN
IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
BEGIN
ROLLBACK
RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
END;
END;
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
END;
My function looks as follows:
CREATE OR ALTER FUNCTION dvd_numb_left(#member_id INT)
RETURNS #tab_dvd_numb_left TABLE(MemberId INT, Name VARCHAR(50), TotalDvdLeft INT, AtTimeDvdLeft INT)
AS
BEGIN
DECLARE #name VARCHAR(50)
DECLARE #dvd_total_left INT
DECLARE #dvd_at_time_left INT
DECLARE #dvd_limit INT
DECLARE #dvd_rented INT
DECLARE #dvd_at_time INT
DECLARE #dvd_on_rent INT
SET #dvd_limit = (SELECT Membership.MembershipLimitPerMonth FROM Membership
WHERE Membership.MembershipId = (SELECT Member.MembershipId FROM Member WHERE Member.MemberId = #member_id))
SET #dvd_rented = (SELECT COUNT(Rental.MemberId) FROM Rental
WHERE CONCAT(month(Rental.RentalShippedDate), '.', year(Rental.RentalShippedDate)) = CONCAT(month(GETDATE()), '.', year(GETDATE())) AND Rental.MemberId = #member_id)
SET #dvd_at_time = (SELECT Membership.DVDAtTime FROM Membership
WHERE Membership.MembershipId = (SELECT Member.MembershipId FROM Member WHERE Member.MemberId = #member_id))
SET #dvd_on_rent = (SELECT COUNT(Rental.MemberId) FROM Rental
WHERE Rental.MemberId = #member_id AND Rental.RentalReturnedDate IS NULL)
SET #name = (SELECT CONCAT(Member.MemberFirstName, ' ', Member.MemberLastName) FROM Member WHERE Member.MemberId = #member_id)
SET #dvd_total_left = #dvd_limit - #dvd_rented
SET #dvd_at_time_left = #dvd_at_time - #dvd_on_rent
IF #dvd_total_left < 0
BEGIN
SET #dvd_total_left = 0
SET #dvd_at_time_left = 0
INSERT INTO #tab_dvd_numb_left(MemberId, Name, TotalDvdLeft, AtTimeDvdLeft)
VALUES(#member_id, #name, #dvd_total_left, #dvd_at_time_left)
RETURN;
END
INSERT INTO #tab_dvd_numb_left(MemberId, Name, TotalDvdLeft, AtTimeDvdLeft)
VALUES(#member_id, #name, #dvd_total_left, #dvd_at_time_left)
RETURN;
END;
Will be glad for any advice.
Your main issue is that even though you populate #TempTable you never pull any values from it.
CREATE OR ALTER TRIGGER trg_Rental_StopDvdShip
ON RENTAL
FOR UPDATE
AS
BEGIN
DECLARE #MemberId INT, #RentalId INT;
-- Move test for column update to the first test as it applies to the entire update, not per row.
IF UPDATE(RentalShippedDate)
BEGIN
SELECT * INTO #TempTable FROM inserted;
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
-- Actually pull some information from #TempTable - this wasn't happening before
SELECT TOP 1 #RentalID = RentalId, #MemberId = MemberId FROM #TempTable;
-- Select our values to its working
-- SELECT #RentalID, #MemberId;
IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
BEGIN
ROLLBACK
RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
END;
-- Delete the current handled row
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
-- For neatness I always drop temp tables, makes testing easier also
DROP TABLE #TempTable;
END;
END;
An easy way to debug simply triggers like this is to copy the T-SQL out and then create an #Inserted table variable e.g.
DECLARE #Inserted table (RentalId INT, MemberId INT);
INSERT INTO #Inserted (RentalId, MemberId)
VALUES (1, 1), (2, 2);
DECLARE #MemberId INT, #RentalId INT;
-- Move test for column update to the first test as it applies to the entire update, not per row.
-- IF UPDATE(RentalShippedDate)
BEGIN
SELECT * INTO #TempTable FROM #inserted;
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
-- Actually pull some information from #TempTable - this wasn't happening before
SELECT TOP 1 #RentalID = RentalId, #MemberId = MemberId FROM #TempTable;
-- Select our values to its working
SELECT #RentalID, #MemberId;
-- IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
-- BEGIN
-- ROLLBACK
-- RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
-- END;
-- Delete the current handled row
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
-- For neatness I always drop temp tables, makes testing easier also
DROP TABLE #TempTable;
END;
Note: throw is the recommended way to throw an error instead of raiserror.
Another thing to consider is that you must try to transform your UDF into an inline TVF because of some side effects.
Like this one:
CREATE OR ALTER FUNCTION dvd_numb_left(#member_id INT)
RETURNS TABLE
AS
RETURN
(
WITH
TM AS
(SELECT Membership.MembershipLimitPerMonth AS dvd_limit,
Membership.DVDAtTime AS dvd_at_time,
CONCAT(Member.MemberFirstName, ' ', Member.MemberLastName) AS [name]
FROM Membership AS MS
JOIN Member AS M
ON MS.MembershipId = M.MembershipId
WHERE M.MemberId = #member_id
),
TR AS
(SELECT COUNT(Rental.MemberId) AS dvd_rented
FROM Rental
WHERE YEAR(Rental.RentalShippedDate ) = YEAR(GETDATE)
AND MONTH(Rental.RentalShippedDate ) = MONTH(GETDATE)
AND Rental.MemberId = #member_id
)
SELECT MemberId, [Name],
CASE WHEN dvd_limit - dvd_rented < 0 THEN 0 ELSE dvd_limit - dvd_rented END AS TotalDvdLeft,
CASE WHEN dvd_limit - dvd_rented < 0 THEN 0 ELSE dvd_at_time - dvd_on_rent END AS AtTimeDvdLeft
FROM TM CROSS JOIN TR
);
GO
Which will be much more efficient.
The absolute rule to have performances is: TRY TO STAY IN A "SET BASED" CODE instead of iterative code.
The above function can be optimized by the optimzer whilet yours cannot and will needs 4 access to the same tables.
I'm trying to generate dummy data from the existing data I have in the tables. All I want is to increase the number of records in Table1 to N specified amount. The other tables should increase based on the foreign key references.
The tables has one to many relationship. For one record in table 1, I can have multiple entries in table 2, and in table 3 I can have many records based on IDs of the second table.
Since IDs are primary keys, I either capture it by
SET #NEWLY_INSERTED_ID = SCOPE_IDENTITY()
after inserting to table 1 and using in insert for table2, or inserting them to temp table and joining them to achieve the same results for table 3.
Here's the approach I'm taking with the CURSOR.
DECLARE #MyId as INT;
DECLARE #myCursor as CURSOR;
DECLARE #DESIRED_ROW_COUNT INT = 70000
DECLARE #ROWS_INSERTED INT = 0
DECLARE #CURRENT_ROW_COUNT INT = 0
DECLARE #NEWLY_INSERTED_ID INT
DECLARE #LANGUAGE_PAIR_IDS TABLE ( LangugePairId INT, NewId INT, SourceLanguage varchar(100), TargetLangauge varchar(100) )
WHILE (#ROWS_INSERTED < #DESIRED_ROW_COUNT)
BEGIN
SET #myCursor = CURSOR FOR
SELECT Id FROM MyTable
SET #CURRENT_ROW_COUNT = (SELECT COUNT(ID) FROM MyTable)
OPEN #myCursor;
FETCH NEXT FROM #myCursor INTO #MyId;
WHILE ##FETCH_STATUS = 0
BEGIN
IF ((#CURRENT_SUBMISSION_COUNT < #DESIRED_ROW_COUNT) AND (#ROWS_INSERTED < #DESIRED_ROW_COUNT))
BEGIN
INSERT INTO [dbo].[MyTable]
([Column1]
([Column2]
([Column3]
)
SELECT
,convert(numeric(9,0),rand() * 899999999) + 100000000
,COlumn2
,Colum3
FROM MyTable
WHERE Id = #MyId
SET #NEWLY_INSERTED_ID = SCOPE_IDENTITY()
INSERT INTO [dbo].[Language]
([MyTable1Id]
,[Target]
,[Source]
OUTPUT inserted.Id, inserted.MyTable1Id, inserted.Source, inserted.[Target] INTO #LANGUAGE_PAIR_IDS (LangugePairId, NewId, SourceLanguage, TargetLangauge)
SELECT
#NEWLY_INSERTED_ID
,[Target]
,[Source]
FROM [dbo].[Language]
WHERE MyTableId = #MyId
ORDER BY Id
DECLARE #tbl AS TABLE (newLanguageId INT, oldLanguageId INT, sourceLanguage VARCHAR(100), targetLanguage VARCHAR(100))
INSERT INTO #tbl (newLanguageId, oldLanguageId, sourceLanguage, targetLanguage)
SELECT 0, id, [Source], [Target] MyTable1Id FROM Language WHERE MyTable1Id = #MyId ORDER BY Id
UPDATE t
SET t.newlanguageid = lp.LangugePairId
FROM #tbl t
JOIN #LANGUAGE_PAIR_IDS lp
ON t.sourceLanguage = lp.SourceLanguage
AND t.targetLanguage = lp.TargetLangauge
INSERT INTO [dbo].[Manager]
([LanguagePairId]
,[UserId]
,[MyDate])
SELECT
tbl.newLanguageId
,p.[UserId]
,p.[MyDate]
FROM Manager m
INNER JOIN #tbl tbl
ON m.LanguagePairId = tbl.oldLanguageId
WHERE m.LanguagePairId in (SELECT Id FROM Language WHERE MyTable1Id = #MyId) -- returns the old language pair id
SET #ROWS_INSERTED += 1
SET #CURRENT_ROW_COUNT +=1
END
ELSE
BEGIN
PRINT 'REACHED EXIT'
SET #ROWS_INSERTED = #DESIRED_ROW_COUNT
BREAK
END
FETCH NEXT FROM #myCursor INTO #MyId;
END
CLOSE #myCursor
DEALLOCATE #myCursor
END
The above code works! It generates the data I need. However, it's very very slow. Just to give some comparison. Initial load of data for table 1 was ~60,000 records, Table2: ~74,000 and Tabl3 ~3,400
I tried to insert 9,000 rows in Table1. With the above code, it took 17:05:01 seconds to complete.
Any suggestion on how I can optimize the query to run little faster? My goal is to insert 1-2 mln records in Table1 without having to wait for days. I'm not tied to CURSOR. I'm ok to achieve the same result in any other way possible.
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.
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.