views:

632

answers:

4

Hi all,

Suppose i have the following table

book
-----
id
name
genre
check_out_date
location_id

Can anyone post an example of an efficient stored procedure that only updates the book table where the passed in values are different from the current values in the table?

i.e. if i passed in values ( 1, "Slaughterhouse V", "Fiction", 10/10/2008, 54) and the values in the table are (1, "Slaughterhouse V, "Fiction", 10/24/2009, 70), it would only update the check_out_date and the location_id columns.

Update Book set check_out_date=@check_out_date (10/24/2009), location_id=@location_id (70).

Thanks in advance

+2  A: 
UPDATE book
SET name = @name,
    genre = @genre,
    check_out_date = @check_out_date,
    location_id = @location_id
FROM BOOK
WHERE (id = @id) AND
     (((@name IS NULL AND name IS NOT NULL) OR
       (@name IS NOT NULL AND name IS NULL) OR
       (@name IS NOT NULL AND name IS NOT NULL AND name <> @name)) OR 
      ((@genre IS NULL AND genre IS NOT NULL) OR
       (@genre IS NOT NULL AND genre IS NULL) OR
       (@genre IS NOT NULL AND genre IS NOT NULL AND genre <> @genre)) OR 
     ((@check_out_date IS NULL AND check_out_date IS NOT NULL) OR
       (@check_out_date IS NOT NULL AND check_out_date IS NULL) OR
       (@check_out_date IS NOT NULL AND check_out_date IS NOT NULL AND
        check_out_date <> @check_out_date)) OR 
     ((@location_id IS NULL AND location_id IS NOT NULL) OR
       (@location_id IS NOT NULL AND location_id IS NULL) OR
       (@location_id IS NOT NULL AND location_id IS NOT NULL AND
        location_id <> @location_id)))
John Saunders
You may need to do null checking, but that might be overboard.
Charles Graham
Just be warned that this will not update any row where any of those column values are null or any of the parameter values are null (which means that if any parameters are null, NO rows will be updated)
Adam Robinson
@Charles and @Adam: Picky, Picky... ;-)
John Saunders
Just trying to keep you honest ;)
Charles Graham
I have no idea what any of that means. But assuming it does what AlteredConcept wants, would that really be more efficient than just updating the entire row? I mean, is there a legitimate reason to do this sort of check before an update?
Calvin
@Calvin: Yes. Performing an unnecessary update is bad practice and can have serious implications when you get into transaction-based technologies like replication. I can't tell you how many times I've had to resolve replication conflicts that arose over nothing more than automatic, unnecessary updates in legacy code.
Adam Robinson
Thanks john, but this will only work if one of the values has changed.What i'd like to be able to do is compare the values passed in verses the values in the table and then update only the columns where the values are different. I elaborated a bit more in the initial post.Take care!
AlteredConcept
A: 

The most efficient option would be to make your comparisons in the BL or possibly DAL and make the update-or-not decision there.

You're probably reading the record anyway if you know it's an update not an insert. And there's probably other validation too. Handle it all in one place.

le dorfier
So is doing this via sproc generally slower? If so how much slower? Writing code to compare values in the app for each object is certainly doable, but I have 20 other classes which would probably need the same comparison done as well.
AlteredConcept
If you test in the SP, you have to execute the SP for each row. The load of an SP you don't call in 0. And you're going to have to write the logic in one or the other. I think you can see it would be easier in a more expressive language like C# rather than TSQL. Same difference x 20.
le dorfier
So i would probably need to change my BookDAL method Update(Book b) to Update(Book updatedBook, Book originalBook), and then compare the object and update only the changes within that method right? Sorry if i am asking too many questions, but i'm new to the oop concept. I'm used to just taking the updated values and sending them directly to the sp without creating an object.Thanks
AlteredConcept
Often if you have a Book object you would already have a BookDAL object at hand based on having queried the database to find out if it exists; and the column values would be assigned to the Book object properties. Those are what would be compared with the UI or wherever the new field values are coming from. It's not unusual to have a "dirty" property that's set whenever an object property (name, title, whatever) setter sees a value that doesn't match the existing value.
le dorfier
Do you have any examples on how to create a dirty property for each object property? That would definitely make things <b>alot</b> easier cause then i can do the comparison without another object.
AlteredConcept
Poor answer. Without locking, the values in the database could change during your comparison.
Some Canuck
You may think it's poor, but it's unquestionably the most common pattern. The question was to determine if *the current user* changed the field values, and choosing whether to update or not based on what *this user* is doing. If some other user is changing it doesn't affect whether or not *this user* is making changes. The most common way to deal with what others do is to raise an error if the source record has changed when you update (which is built into ADO.NET etc. if you choose to use it )
le dorfier
And if there's no UI, and it's all done at once, just use the equivalent of BEGIN TRANSACTION / SELECT ... / compare, and if different /UPDATE ... / COMMIT TRANSACTION if there's significant risk of collisions.
le dorfier
A: 

You might not want to do this, but you could also first DELETE the record with the matching ID and then INSERT it again.

edosoft
A: 

You said a stored procedure, so no reason not to keep it simple and do separate updates.

UPDATE book SET name = @name WHERE id = @id and name <> coalesce(@name,name)
UPDATE book SET genre = @genre WHERE id = @id and genre <> coalesce(@genre,genre)
UPDATE book SET check_out_date = @check_out_date WHERE id = @id and check_out_date <> coalesce(@check_out_date,check_out_date)
UPDATE book SET location_id WHERE id = @id and location_id <> coalesce(@location_id,location_id)
Some Canuck
This still doesn't deal with NULLs correctly. It will update when it was NULL and is still NULL. And whether it's in a stored procedure or not has nothing to do with the wisdom of one SQL statement per column.
le dorfier
Updated to account for nulls, and still, it's a better answer to perform multiple updates within the same stored procedure than it is to rely on the application.
Some Canuck