views:

332

answers:

3

Hi,

I have the following sql statement where i'm trying to update multiple rows matching a select statement.

UPDATE Cars
SET engineSize = CASE specCode WHEN 1 THEN value ELSE engineSize END
FROM Cars
INNER JOIN CarSpecs ON CarsSpecs.carID = Cars.carID

My tables are as follows:

Cars

carID   engineSize ...
1       0
2       0

CarSpecs

carID   specCode    value
1       1           1800
1       2           Blue
1       3           Petrol
2       1           2200
2       2           Green
2       3           Petrol

specCode relates to a type of specification I want to update in the Cars table (1 being the engine size)

When I run the query it comes back NULL everytime. The way I see it it should find the specCode = 1 and set the engineSize to 1800 then after it's set it just sets it to the first found value.

Any ideas?

Edit: I need to update multiple columns in Cars table. That's the reason for using CASE, ie:

UPDATE Cars 
SET engineSize = CASE specCode WHEN 1 THEN value ELSE engineSize END,
colour = CASE specCode WHEN 2 THEN value ELSE colour END
FROM Cars
INNER JOIN CarSpecs ON CarsSpecs.carID = Cars.carID
+1  A: 

It looks like you don't need a CASE anyway. Does this work?

UPDATE Cars
SET Cars.engineSize = CarsSpecs.value 
FROM Cars
INNER JOIN CarSpecs ON CarsSpecs.carID = Cars.carID AND  CarsSpecs.specCode =1

The PIVOT solution (if needed)

UPDATE Cars 
SET engineSize = ISNULL([1],engineSize),
colour = ISNULL([2],colour)
FROM Cars
INNER JOIN 
(SELECT * FROM 
dbo.CarSpecs 
PIVOT (MAX(VALUE) FOR  SPECCODE IN ([1],[2])) p) Pivoted ON Pivoted.CARID = Cars.CarId
Martin Smith
Ah yes, I forgot to mention I need to update multiple columns, see edit. thanks.
CL4NCY
Run the query 3 times, one each for each spec. Then Martin's solution works.
Obalix
or you could maybe PIVOT the CarSpecs table and join onto the result of that for your update. Failing that have a look at the Query Execution plan to see what's happening. I'm a bit uncertain as to the whole idea of UPDATING the same row in Cars multiple times in one statement.
Martin Smith
+3  A: 

To update multiple columns, in your case you need multiple joins (as sketched by Martin)

UPDATE Cars
SET  engineSize = CarSpecs1.value, colour = CarSpecs2.value
FROM Cars
INNER JOIN CarSpecs CarSpecs1 
    ON CarsSpecs1.carID = Cars.carID AND CarsSpecs1.specCode =1
INNER JOIN CarSpecs CarSpecs2 
    ON CarsSpecs2.carID = Cars.carID AND CarsSpecs2.specCode =2

Use OUTER joins of not every spec is stored for each car.

devio
Good one, did not thik of that !
Obalix
Yep. More straight forward than the PIVOT suggestion I just made.
Martin Smith
This will do the job, however I need to update 20 columns on up to 10,000 cars so I can't help but feel LEFT joins would slow this right down. Is there no way to do this with CASE statements at all?
CL4NCY
No. Have a look at the Query Execution Plan on your statement. It ends up using a GROUP BY on CarSpecs.CarId thus reducing it to one row way before the update happens. I presume that whatever aggregation it uses ends up with NULL as the "speccode" and "value" values for each CarId.
Martin Smith
In this case, consider Martin's solution, and just execute one statement for each of the properties, i.e. begin transaction, update cars for prop 1, update cars fror prop2, ..., commit trans.
Obalix
A: 

In case that Martin's solution does not work (when it is run three times) you could do it also at once ... although a bit more arkward:

UPDATE Cars
SET Cars.engineSize = (SELECT value FROM CarSpecs WHERE CarSpecs.carId = Cars.carID AND CarSpecs.specCode = 1),
SET Cars.colour = (SELECT value FROM CarSpecs WHERE CarSpecs.carId = Cars.carID AND CarSpecs.specCode = 2),
SET Cars.fuel = (SELECT value FROM CarSpecs WHERE CarSpecs.carId = Cars.carID AND CarSpecs.specCode = 3)
FROM Cars
Obalix