51

I have a performance-related question. Let's say I have a user with first name Michael. Take the following query:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

Will the query actually execute the update, even though it is being updated to the same value? If so, how do I prevent it from happening?

1

4 Answers 4

58

Due to the MVCC model of Postgres, and according to the rules of SQL, an UPDATE writes a new row version for every row that is not excluded in the WHERE clause.

This does have a more or less substantial impact on performance, directly and indirectly. "Empty updates" have the same cost per row as any other update. They fire triggers (if present) like any other update, they have to be WAL-logged and they produce dead rows bloating the table and causing more work for VACUUM later like any other update.

Index entries and TOASTed columns where none of the involved columns are changed can stay the same, but that is true for any updated row. Related:

It's almost always a good idea to exclude such empty updates (when there is an actual chance it may happen). You did not provide a table definition in your question. We have to assume first_name can be NULL (which wouldn't be surprising for a "first name"), hence the query has to use NULL-safe comparison:

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND    first_name IS DISTINCT FROM 'Michael';

If first_name IS NULL before the update, a test with just first_name <> 'Michael' would evaluate to NULL and as such exclude the row from the update. Sneaky error. If the column is defined NOT NULL, use the simple equality check, though, that's a bit cheaper.

Related:

2
  • I'm asking you to clarify (justify) the claim in the first sentence in your answer (that's what the comments section on answers is for afaiu). It's not at all clear to me why MVCC would force this operational behavior on postgres. I suspect triggers, some minutiae re. replication, or some other more boring concerns are what actually preclude pg from avoiding generation of dead tuples when an update is a noop.
    – jberryman
    May 24, 2018 at 10:22
  • 3
    @jberryman: I don't actually know the reasons why the project went this way. That was established long ago. But I assume it would be needlessly expensive to check every row for equality and have a separate code-path for unchanged rows. The handling of transaction-IDs would be more complicated - special casing for rollback, snapshot handling, lock management, WAL, an what not ... May 24, 2018 at 11:29
6

ORM's like Ruby on Rail's offer deferred execution which mark a record as changed (or not) and then when needed or called, then submit the change to the database.

PostgreSQL is a database and not an ORM. It would have decreased performance if it took the time to check if a new value was the same as the updated value in your query.

It will therefore update the value regardless of whether it is the same as the new value or not.

If you wish to prevent this, you could use code like Max Vernon suggested in his answer.

2

You could simply add to the where clause:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

If first_name is defined as NOT NULL, the OR first_name IS NULL part can be removed.

The condition:

(first_name <> 'Michael' OR first_name IS NULL)

can also be written more elegantly as (in Erwin's answer):

first_name IS DISTINCT FROM 'Michael'
0
1

From a database point of view

The answer to your question is YES. The update will take place. The database does not check the previous value, it only sets the new value.

As this happen in memory (and will only be written to the datafiles after a commit is issued) the performance would not be an issue.

From an ORM perspective

Normally you will have an Object representing a single row of the database (it can be a lot more complex than that, but let's keep it simple). This object is managed in memory (at the app server level) and only the latest commited version of that object will actually make it to the database at a certain point.

That may explain the different behaviour.

Now, let's not compare a cargo ship with a 3D printer. The fact that you can send 3D printers using cargo ships doesn't mean that there may be any kind of comparison between them.

Enjoy!

I hope this clarified some concepts.

4
  • 5
    Performance is and issue. Every update has to be written on disk (the log and the table). Oct 16, 2015 at 7:01
  • It will depend on the actual RDBMS you use. But most of them do not commit every single update, but only the last commited block they have in memory. You never read or write a single row in a database. You read/write blocks and keep them in memory until you have to flush it out to put a new block in the same place. While in memory, not every change in a row will be written to disk, but only the block contents when the "database writer" process is signaled to dump that memory block into a datafile. So, no... Is not an issue unless your application holds the block uncommited for too long.
    – Silvarion
    Oct 16, 2015 at 12:30
  • 1
    the question is about Postgres, not about any arbitrary DBMS. And while the updates do not all have to be written one by one, every write on the database has to be written to the log. If a change is not written on persistent storage, how will the DBMS survive a system crash? Oct 16, 2015 at 13:19
  • Yes, it writes in the logs, from memory as well during checkpoints. Unless you have an awfully huge number of concurrent users, it shouldn't be a problem at all. Logs are written in batches as well. I think we're talking about servers. If you are talking about a Postgres database in a laptop with a 5400RPM HDD, yes... you will always have performance issues. So, the final answer would be the first one... It depends on too many things.
    – Silvarion
    Oct 16, 2015 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.