Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 Transact-SQL (2000)
 is ther an alternative to a cursor required?

Author  Topic 

chessclock
Starting Member

8 Posts

Posted - 2006-05-29 : 14:00:47
/* Good day to everyone! Can somebody advise me please: will this cursor work? The problem is using SELECT statement within a cursor,
retreiving @StockTicketNumber for every record, and retrieving
appropriate totals for every @StockTicketNumber.
If the cursor does not work, is there any alternative
(like Temp tables, etc.) for it! Thank you ALOT!!! */


DECLARE @unit_cost money, @WholesalePrice
money, @SUM_WholesalePrice money,
@SUM_VehicleSalvageCost money,
@StockTicketNumber text

/* here is the cursor itself */

@SUM_WholesalePrice money = 1 /* avoiding division by zero */
@SUM_VehicleSalvageCost money = 0

DECLARE get_unit_cost_cursor CURSOR

SET get_unit_cost_cursor = CURSOR FOR SELECT
UnitCost, WholesalePrice, StockTicketNumber
FROM Inventory

OPEN get_unit_cost_cursor

FETCH NEXT FROM get_unit_cost_cursor INTO @unit_cost

/* here is the loop */
WHILE (@@FETCH_STATUS = 0)

BEGIN
IF @unit_cost = 0

/* now getting the sum 1 and 2 using
subqueries, then calculating and updating */

UPDATE Inventory SET UnitCost = @SUM_VehicleSalvageCost * @WholesalePrice / @SUM_WholesalePrice
WHERE (CURRENT OF get_unit_cost_cursor) AND
@SUM_WholesalePrice = (SELECT SUM(WholesalePrice) FROM Inventory WHERE StockTicketNumber = @StockTicketNumber)
AND @SUM_VehicleSalvageCost = (SELECT SUM(VehicleSalvageCost) FROM VehicleStockDetails WHERE StockTicketNumber = @StockTicketNumber)

/* ELSE no update is taking place */

FETCH NEXT FROM get_unit_cost_cursor INTO @unit_cost
END

CLOSE get_unit_cost_cursor
DEALLOCATE @get_unit_cost_cursor

Srinika
Master Smack Fu Yak Hacker

1378 Posts

Posted - 2006-05-29 : 14:24:32
Its the trend of some "Programmers" <including me - sometime back> to think in terms of a cursor whenever some T-SQL needs to be written. Its easier to think as a standard program (written using VB / Java /...) and consider one record at a time, getting values to variables and assigning those etc.

eg. if u want to increase the salary of employee table by 10 % u can use a single SQL query or u can (but don't) use a cursor where each record is fed to variables, the salary value is multiplied by 1.1 and assigned to another table, then updating the record with the new value and do for each record in a loop.
The cursor option kills time.

But if u properly use SQL / T-SQL, u can avoid the cursors in most situations.
Cursors should be avoided whenever possible, as those are very very inefficient.

Again temp tables are used if there is a strong need for that.

Ur requirements are not clear to give a solution, but u may use something like

UPDATE Inventory SET UnitCost = (<query to find sum of VehicleSalvageCost> * (<query to find WholesalePrice > / (<query to find sum of WholesalePrice >
WHERE (CURRENT OF get_unit_cost_cursor) AND
......


May be u need some 'joins", but with the information u provided its very hard to tell


Srinika
Go to Top of Page

cmdr_skywalker
Posting Yak Master

159 Posts

Posted - 2006-05-29 : 14:32:00
You can try a recordset solution instead of cursor based.
Try the following. I used the case statement because returning a 1 instead of zero and the numerator is nonzero, it will return nonzero value which is wrong.

UPDATE
SET UnitCost =
CASE
WHEN SUM_WholesalePrice = 0 THEN 0
ELSE
(SUM_VehicleSalvageCost * WholesalePrice) / SUM_WholesalePrice
END
FROM
Inventory I,
(SELECT SUM(WholesalePrice) AS SUM_WholesalePrice
FROM Inventory
WHERE StockTicketNumber = i.StockTicketNumber
),
(SELECT SUM(VehicleSalvageCost) AS SUM_VehicleSalvageCost
FROM VehicleStockDetails
WHERE StockTicketNumber = i.StockTicketNumber
)
WHERE
I.UnitCost = 0


WHERE (CURRENT OF get_unit_cost_cursor) AND
@SUM_WholesalePrice = (SELECT SUM(WholesalePrice) FROM Inventory WHERE StockTicketNumber = @StockTicketNumber)
AND @SUM_VehicleSalvageCost = (SELECT SUM(VehicleSalvageCost) FROM VehicleStockDetails WHERE StockTicketNumber = @StockTicketNumber)


May the Almighty God bless us all!
Go to Top of Page

cmdr_skywalker
Posting Yak Master

159 Posts

Posted - 2006-05-29 : 14:36:05
I forgot to put the alias. I wasn't sure if it will work without the alias in the table. At anyrate, the final query should look like this:

UPDATE
SET UnitCost =
CASE
WHEN W.SUM_WholesalePrice = 0 THEN 0
ELSE
(V.SUM_VehicleSalvageCost * WholesalePrice) / W.SUM_WholesalePrice
END
FROM
Inventory I,
(SELECT SUM(WholesalePrice) AS SUM_WholesalePrice
FROM Inventory
WHERE StockTicketNumber = i.StockTicketNumber
)AS W,
(SELECT SUM(VehicleSalvageCost) AS SUM_VehicleSalvageCost
FROM VehicleStockDetails
WHERE StockTicketNumber = i.StockTicketNumber
) AS V
WHERE
I.UnitCost = 0


May the Almighty God bless us all!
Go to Top of Page

chessclock
Starting Member

8 Posts

Posted - 2006-05-30 : 06:42:22
Thank you guys for every single line you have posted!
Lots of food for thought.
In fact, I used to use VB6 and ASP, but now I need to process (and update) 300,000 records within a few hours on a Saturday afternoon. This is why I decided to use those 'server tools', as opposed to DataBase connectors.

Comments on the solution, if it does not look clear:
Every Inventory (table) record has a WholesalePrice (field) used in a calculation of UnitCost (flield to be updated). Also, for every record, two totals (used in a calculation of UnitCost) must be calculated after querying two separate tables (Inventory and VehicleStockDetails) by a StockTicketNumber corresponding to the CURRENT record. This is why two queries are kind of connected to the cursor. In MS docs I have found that I can't use more that one SELECT statement when using a cursor. This is why I suspect that my solution will not work!..

quote:
Originally posted by Srinika

Its the trend of some "Programmers" <including me - sometime back> to think in terms of a cursor whenever some T-SQL needs to be written. Its easier to think as a standard program (written using VB / Java /...) and consider one record at a time, getting values to variables and assigning those etc.

eg. if u want to increase the salary of employee table by 10 % u can use a single SQL query or u can (but don't) use a cursor where each record is fed to variables, the salary value is multiplied by 1.1 and assigned to another table, then updating the record with the new value and do for each record in a loop.
The cursor option kills time.

But if u properly use SQL / T-SQL, u can avoid the cursors in most situations.
Cursors should be avoided whenever possible, as those are very very inefficient.

Again temp tables are used if there is a strong need for that.

Ur requirements are not clear to give a solution, but u may use something like

UPDATE Inventory SET UnitCost = (<query to find sum of VehicleSalvageCost> * (<query to find WholesalePrice > / (<query to find sum of WholesalePrice >
WHERE (CURRENT OF get_unit_cost_cursor) AND
......



Go to Top of Page

chessclock
Starting Member

8 Posts

Posted - 2006-05-30 : 06:54:29
Thank you for the idea!
Is the query below the one for a recordset solution (to use with VB6, ASP, etc.), or it is a part of a cursor-based solution (to be used in a stored procedure)? - I am asking it because in your first reply you have posted a cursor statement (the one on the bottom).

quote:
Originally posted by cmdr_skywalker

I forgot to put the alias. I wasn't sure if it will work without the alias in the table. At anyrate, the final query should look like this:

UPDATE
SET UnitCost =
CASE
WHEN W.SUM_WholesalePrice = 0 THEN 0
ELSE
(V.SUM_VehicleSalvageCost * WholesalePrice) / W.SUM_WholesalePrice
END
FROM
Inventory I,
(SELECT SUM(WholesalePrice) AS SUM_WholesalePrice
FROM Inventory
WHERE StockTicketNumber = i.StockTicketNumber
)AS W,
(SELECT SUM(VehicleSalvageCost) AS SUM_VehicleSalvageCost
FROM VehicleStockDetails
WHERE StockTicketNumber = i.StockTicketNumber
) AS V
WHERE
I.UnitCost = 0


May the Almighty God bless us all!

Go to Top of Page

rockmoose
SQL Natt Alfen

3279 Posts

Posted - 2006-05-30 : 13:28:02
skywalker, you are mixing derived tables and correlated subqueries in the sql.

-- derived tables
select	I.UnitID
,I.StockTicketNumber
,I.WholesalePrice
,UnitCost = (I.WholesalePrice * sum1.SUM_WholesalePrice) / sum2.SUM_VehicleSalvageCost
-- instead of select, you can update by using the 2 lines below
-- update I set
-- UnitCost = (I.WholesalePrice * sum1.SUM_WholesalePrice) / sum2.SUM_VehicleSalvageCost
from dbo.Inventory as I
join
(
select StockTicketNumber
,sum(WholesalePrice) as SUM_WholesalePrice
from dbo.Inventory
group by
StockTicketNumber
) as sum1
on I.StockTicketNumber = sum1.StockTicketNumber
join
(
select StockTicketNumber
,sum(VehicleSalvageCost) as SUM_VehicleSalvageCost
from dbo.VehicleStockDetails
group by
StockTicketNumber
having sum(VehicleSalvageCost) <> 0 -- one way of avoiding / 0
) as sum2
on I.StockTicketNumber = sum2.StockTicketNumber


-- correlated subqueries
update	dbo.Inventory set
UnitCost =
( I.WholesalePrice
* ( select sum(VehicleSalvageCost)
from dbo.VehicleStockDetails as V
where dbo.Inventory.StockTicketNumber = V.StockTicketNumber)
)
/ ( select coalesce(nullif(sum(WholesalePrice),0),1) -- avoid / 0, by setting 0 to null and null to 1
from dbo.Inventory as I
where dbo.Inventory.StockTicketNumber = I.StockTicketNumber)


rockmoose
Go to Top of Page

chessclock
Starting Member

8 Posts

Posted - 2006-05-31 : 07:28:32
Rockmoose,

Thank you for a lesson of brilliant T-SQL programing.
A (small network, part-time) admin, now I'd really like to learn it, not just finishing a single minor project and forgetting about it.

Although both pieces of code look very impressive, can you please tell if my assumptions are right:

1) having sum(WholesalePrice) <> 0
will make the execution of formula skip every time
sum(WholesalePrice) = 0
,in other words the UnitCost won't be calculated;

2) for the Derived Table Code, running a stored procedure containing this code will do the task of updating the UnitCost field for all the records in the original Inventory table as it will create a temporary table of (all my) 300,000 records:
no 'external' VB or ASP script with the corresponding recordSet is required

3) for the Correlated Subqueries Code, running a stored procedure containing this code require
either using an 'external' VB or ASP script with the corresponding recordSet,
or using a cursor executing this stored procedure, like the following (here is only the cursor part itself):

DECLARE get_CostPrice_cursor CURSOR

SET get_CostPrice_cursor = CURSOR FOR SELECT CostPrice, WholesalePrice, StockTicketNumber, UnitID FROM Inventory

OPEN get_CostPrice_cursor

FETCH NEXT FROM get_CostPrice_cursor INTO @CostPrice, @WholesalePrice, @StockTicketNumber, @UnitID

WHILE (@@FETCH_STATUS = 0)

BEGIN
IF @CostPrice = 0

EXEC AK_UnitCost_StoredProcedure @WholesalePrice, @StockTicketNumber, @UnitID
END
/* ELSE no update is taking place */

FETCH NEXT FROM get_CostPrice_cursor INTO @CostPrice, @WholesalePrice, @StockTicketNumber, @UnitID

END

CLOSE get_CostPrice_cursor
DEALLOCATE @get_CostPrice_cursor

/* Best regards,
Chessclock */


quote:
Originally posted by rockmoose

skywalker, you are mixing derived tables and correlated subqueries in the sql.

-- derived tables
select	I.UnitID
,I.StockTicketNumber
,I.WholesalePrice
,UnitCost = (I.WholesalePrice * sum2.SUM_VehicleSalvageCost / sum1.SUM_WholesalePrice)
-- instead of select, you can update by using the 2 lines below
-- update I set
-- UnitCost = (I.WholesalePrice * sum2.SUM_VehicleSalvageCost / sum1.SUM_WholesalePrice)
from dbo.Inventory as I
join
(
select StockTicketNumber
,sum(WholesalePrice) as SUM_WholesalePrice
from dbo.Inventory
group by
StockTicketNumber

having sum(WholesalePrice) <> 0 -- one way of avoiding / 0
) as sum1
on I.StockTicketNumber = sum1.StockTicketNumber
join
(
select StockTicketNumber
,sum(VehicleSalvageCost) as SUM_VehicleSalvageCost
from dbo.VehicleStockDetails
group by
StockTicketNumber

) as sum2
on I.StockTicketNumber = sum2.StockTicketNumber


-- correlated subqueries
update	dbo.Inventory set
UnitCost =
( I.WholesalePrice
* ( select sum(VehicleSalvageCost)
from dbo.VehicleStockDetails as V
where dbo.Inventory.StockTicketNumber = V.StockTicketNumber)
)
/ ( select coalesce(nullif(sum(WholesalePrice),0),1) -- avoid / 0, by setting 0 to null and null to 1
from dbo.Inventory as I
where dbo.Inventory.StockTicketNumber = I.StockTicketNumber)


rockmoose

Go to Top of Page

cmdr_skywalker
Posting Yak Master

159 Posts

Posted - 2006-05-31 : 20:03:07
It does not work?

May the Almighty God bless us all!
Go to Top of Page

cmdr_skywalker
Posting Yak Master

159 Posts

Posted - 2006-05-31 : 20:25:10
rockmoose, you're right. i forgot that the table resolution in a FROM SECTION is made within the subquery only and thus, does not recognized correlate with the other table alias.

May the Almighty God bless us all!
Go to Top of Page

chessclock
Starting Member

8 Posts

Posted - 2006-06-01 : 08:17:44
Rockmoose, Cmdr_skywalker,
thank you for your help. Without it, I would have no choice but using a basic script with RecordSet specified and a simple StoredProcedure for record update:

create procedure dbo.updatecostpricebyinventoryid 
(
@InventoryID int,
@CostPrice money
)
as
set nocount on

update dbo.INVENTORY
set CostPrice = @CostPrice
where (InventoryID = @InventoryID)
if (@@rowcount = 0) begin
raiserror(55000, 16, 1)
return (-6)
end

set nocount off
return (0)


Now, making it work only by using a SP only (like the one Rockmoose wrote with the DerivedTable code) on the server is a masterpiece. I would be very grateful if you tell me -

1) can I only use a stored procedure (with the DerivedTable code) and execute it OR do I have to use any RecordSet stuff in an 'external' VB or ASP script?

2) can I add a CASE operator into the Derived Table Code (to check if the UnitCost is 0 and can be modified) without messing up the update, like this:


select I.InventoryID
,I.StockTicketNumber
,I.WholesalePrice
,UnitCost =
CASE
WHEN I.UnitCost > 0 THEN I.UnitCost
ELSE
(I.WholesalePrice * (sum2.SUM_VehicleSalvageCost + 1200) / sum1.SUM_WholesalePrice)
END
from dbo.Inventory as I
join
(
select StockTicketNumber
,sum(WholesalePrice) as SUM_WholesalePrice
from dbo.Inventory
group by
StockTicketNumber

having sum(WholesalePrice) <> 0

) as sum1
on I.StockTicketNumber = sum1.StockTicketNumber
join
(
select StockTicketNumber
,sum(VehicleSalvageCost) as SUM_VehicleSalvageCost
from dbo.Vehicle_Cost_Payment
group by
StockTicketNumber

) as sum2
on I.StockTicketNumber = sum2.StockTicketNumber


quote:
Originally posted by cmdr_skywalker

rockmoose, you're right. i forgot that the table resolution in a FROM SECTION is made within the subquery only and thus, does not recognized correlate with the other table alias.

May the Almighty God bless us all!

Go to Top of Page
   

- Advertisement -