Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is checking rows affected count after database action (insert, update, delete) overkill?

Lately in apps I've been developing I have been checking the number of rows affected by an insert, update, delete to the database and logging an an error if the number is unexpected. For example on a simple insert, update, or delete of one row if any number of rows other than one is returned from an ExecuteNonQuery() call, I will consider that an error and log it. Also, I realize now as I type this that I do not even try to rollback the transaction if that happens, which is not the best practice and should definitely be addressed. Anyways, here's code to illustrate what I mean:

I'll have a data layer function that makes the call to the db:

public static int DLInsert(Person person)
{
    Database db = DatabaseFactory.CreateDatabase("dbConnString");

    using (DbCommand dbCommand = db.GetStoredProcCommand("dbo.Insert_Person"))
    {
        db.AddInParameter(dbCommand, "@FirstName", DbType.Byte, person.FirstName);
        db.AddInParameter(dbCommand, "@LastName", DbType.String, person.LastName);
        db.AddInParameter(dbCommand, "@Address", DbType.Boolean, person.Address);

        return db.ExecuteNonQuery(dbCommand);
    }
}

Then a business layer call to the data layer function:

public static bool BLInsert(Person person)
{
    if (DLInsert(campusRating) != 1)
    {
        // log exception
        return false;
    }

    return true;
}

And in the code-behind or view (I do both webforms and mvc projects):

if (BLInsert(person))
{
    // carry on as normal with whatever other code after successful insert
}
else
{
    // throw an exception that directs the user to one of my custom error pages
}

The more I use this type of code, the more I feel like it is overkill. Especially in the code-behind/view. Is there any legitimate reason to think a simple insert, update, or delete wouldn't actually modify the correct number of rows in the database? Is it more plausible to only worry about catching an actual SqlException and then handling that, instead of doing the monotonous check for rows affected every time?

Thanks. Hope you all can help me out.


UPDATE

Thanks everyone for taking the time to answer. I still haven't 100% decided what setup I will use going forward, but here's what I have taken away from all of your responses.

  • Trust the DB and .Net libraries to handle a query and do their job as they were designed to do.
  • Use transactions in my stored procedures to rollback the query on any errors and potentially use raiseerror to throw those exceptions back to the .Net code as a SqlException, which could handle these errors with a try/catch. This approach would replace the problematic return code checking.

Would there be any issue with the second bullet point that I am missing?

like image 466
ryanulit Avatar asked Apr 30 '12 14:04

ryanulit


People also ask

Does Rowcount work on update?

Using SQL Server @@ROWCOUNTThe statement can be anything that affects rows: SELECT, INSERT, UPDATE, DELETE and so on.

Is update better than delete and insert?

For best future query performance, it's better to do an update to keep the same extents. Delete and insert will not necessarily use the same extents. For a table of that size, it would be unlikely to do so. Furthermore, delete can leave "holes" in your data.

How can I count the number of rows affected in SQL Server?

Usage. SQL Server @@ROWCOUNT is a system variable that is used to return the number of rows that are affected by the last executed statement in the batch.

Which method returns the number of rows affected by the execution of a query?

* @return the number of rows affected by the execution of the SQL query. * @exception Failed to execute the SQL query. The execute() method executes the given SQL query, which may return multiple results. This method returns a boolean (true/false).


2 Answers

I guess the question becomes, "Why are you checking this?" If it's just because you don't trust the database to perform the query, then it's probably overkill. However, there could exist a logical reason to perform this check.

For example, I worked at a company once where this method was employed to check for concurrency errors. When a record was fetched from the database to be edited in the application, it would come with a LastModified timestamp. Then the standard CRUD operations in the data access layer would include a WHERE LastMotified=@LastModified clause when doing an UPDATE and check the record modified count. If no record was updated, it would assume a concurrency error had occurred.

I felt it was kind of sloppy for concurrency checking (especially the part about assuming the nature of the error), but it got the job done for the business.

What concerns me more in your example is the structure of how this is being accomplished. The 1 or 0 being returned from the data access code is a "magic number." That should be avoided. It's leaking an implementation detail from the data access code into the business logic code. If you do want to keep using this check, I'd recommend moving the check into the data access code and throwing an exception if it fails. In general, return codes should be avoided.

Edit: I just noticed a potentially harmful bug in your code as well, related to my last point above. What if more than one record is changed? It probably won't happen on an INSERT, but could easily happen on an UPDATE. Other parts of the code might assume that != 1 means no record was changed. That could make debugging very problematic :)

like image 174
David Avatar answered Nov 15 '22 20:11

David


On the one hand, most of the time everything should behave the way you expect, and on those times the additional checks don't add anything to your application. On the other hand, if something does go wrong, not knowing about it means that the problem may become quite large before you notice it. In my opinion, the little bit of additional protection is worth the little bit of extra effort, especially if you implement a rollback on failure. It's kinda like an airbag in your car... it doesn't really serve a purpose if you never crash, but if you do it could save your life.

like image 20
Brendon Dugan Avatar answered Nov 15 '22 20:11

Brendon Dugan