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.
| 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 retrievingappropriate 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 = 0DECLARE get_unit_cost_cursor CURSORSET get_unit_cost_cursor = CURSOR FOR SELECT UnitCost, WholesalePrice, StockTicketNumber FROM InventoryOPEN 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_costENDCLOSE get_unit_cost_cursorDEALLOCATE @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 likeUPDATE 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 tellSrinika |
 |
|
|
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 ENDFROM 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! |
 |
|
|
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_WholesalePriceENDFROM Inventory I,(SELECT SUM(WholesalePrice) AS SUM_WholesalePriceFROM Inventory WHERE StockTicketNumber = i.StockTicketNumber)AS W,(SELECT SUM(VehicleSalvageCost) AS SUM_VehicleSalvageCost FROM VehicleStockDetails WHERE StockTicketNumber = i.StockTicketNumber) AS VWHEREI.UnitCost = 0 May the Almighty God bless us all! |
 |
|
|
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 likeUPDATE 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 ......
|
 |
|
|
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_WholesalePriceENDFROM Inventory I,(SELECT SUM(WholesalePrice) AS SUM_WholesalePriceFROM Inventory WHERE StockTicketNumber = i.StockTicketNumber)AS W,(SELECT SUM(VehicleSalvageCost) AS SUM_VehicleSalvageCost FROM VehicleStockDetails WHERE StockTicketNumber = i.StockTicketNumber) AS VWHEREI.UnitCost = 0 May the Almighty God bless us all!
|
 |
|
|
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 tablesselect 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_VehicleSalvageCostfrom 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 subqueriesupdate 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 |
 |
|
|
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) <> 0will make the execution of formula skip every timesum(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 required3) 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 CURSORSET get_CostPrice_cursor = CURSOR FOR SELECT CostPrice, WholesalePrice, StockTicketNumber, UnitID FROM InventoryOPEN get_CostPrice_cursor FETCH NEXT FROM get_CostPrice_cursor INTO @CostPrice, @WholesalePrice, @StockTicketNumber, @UnitIDWHILE (@@FETCH_STATUS = 0) BEGIN IF @CostPrice = 0 EXEC AK_UnitCost_StoredProcedure @WholesalePrice, @StockTicketNumber, @UnitIDEND /* ELSE no update is taking place */ FETCH NEXT FROM get_CostPrice_cursor INTO @CostPrice, @WholesalePrice, @StockTicketNumber, @UnitIDENDCLOSE get_CostPrice_cursorDEALLOCATE @get_CostPrice_cursor/* Best regards,Chessclock */quote: Originally posted by rockmoose skywalker, you are mixing derived tables and correlated subqueries in the sql.-- derived tablesselect 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 StockTicketNumberhaving 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 subqueriesupdate 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
|
 |
|
|
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! |
 |
|
|
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! |
 |
|
|
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 StockTicketNumberhaving 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!
|
 |
|
|
|
|
|
|
|