web-dev-qa-db-fra.com

La mise à jour d'une ligne avec la même valeur met-elle réellement à jour la ligne?

J'ai une question liée aux performances. Disons que j'ai un utilisateur avec le prénom Michael. Prenez la requête suivante:

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

La requête exécutera-t-elle réellement la mise à jour, même si elle est mise à jour à la même valeur? Si oui, comment puis-je l'empêcher de se produire?

32
OneSneakyMofo

En raison de modèle MVCC de Postgres, et selon les règles de SQL, un UPDATE écrit une nouvelle version de ligne pour chaque ligne qui n'est pas exclus dans la clause WHERE.

Cela le fait a un impact plus ou moins important sur les performances, directement et indirectement. Les "mises à jour vides" ont le même coût par ligne que toute autre mise à jour. Ils déclenchent des déclencheurs (s'ils sont présents) comme toute autre mise à jour, ils doivent être WAL-logs et ils produisent des lignes mortes gonflant la table et causant plus de travail pour VACUUM plus tard comme toute autre mise à jour .

Indexe les entrées et TOASTed colonnes où aucune des colonnes impliquées n'est modifiée peut reste la même, mais cela est vrai pour toute ligne mise à jour. En relation:

C'est presque toujours une bonne idée d'exclure ces mises à jour vides (lorsqu'il y a une chance réelle que cela se produise). Vous n'avez pas fourni de définition de tableau dans votre question (ce qui est toujours une bonne idée). Nous devons supposer first_name peut être NULL (ce qui ne serait pas surprenant pour un "prénom"), donc la requête doit utiliser NULL-safe comparaison :

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

Si first_name IS NULL avant la mise à jour, un test avec juste first_name <> 'Michael' serait évalué à NULL et exclurait ainsi la ligne de la mise à jour. Erreur sournoise. Si la colonne est définie NOT NULL , utilisez le simple contrôle d'égalité, car c'est un peu moins cher.

En relation:

38
Erwin Brandstetter

ORM comme Ruby sur l'offre différée de Rail qui marque un enregistrement comme modifié (ou non), puis lorsque cela est nécessaire ou appelé, puis soumettez la modification à la base de données.

PostgreSQL est une base de données et non un ORM. Cela aurait diminué les performances s'il avait fallu du temps pour vérifier si une nouvelle valeur était la même que la valeur mise à jour dans votre requête.

Il mettra donc à jour la valeur, qu'elle soit identique ou non à la nouvelle valeur.

Si vous souhaitez empêcher cela, vous pouvez utiliser du code comme Max Vernon l'a suggéré dans sa réponse.

4
Thronk

Vous pouvez simplement ajouter à la clause where:

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

Si first_name est défini comme NOT NULL, le OR first_name IS NULL une partie peut être retirée.

La condition:

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

peut également être écrit plus élégamment comme (dans la réponse d'Erwin):

first_name IS DISTINCT FROM 'Michael'
2
Max Vernon

Du point de vue de la base de données

La réponse à ta question est oui. La mise à jour aura lieu. La base de données ne vérifie pas la valeur précédente, elle définit uniquement la nouvelle valeur.

Comme cela se produit dans la mémoire (et ne sera écrit dans les fichiers de données qu'une fois la validation émise), les performances ne seront pas un problème.

Du point de vue ORM

Normalement, vous aurez un objet représentant une seule ligne de la base de données (il peut être beaucoup plus complexe que cela, mais restons simples). Cet objet est géré en mémoire (au niveau du serveur d'applications) et seule la dernière version validée de cet objet parviendra réellement à la base de données à un certain moment.

Cela peut expliquer les différents comportements.

Maintenant, ne comparons pas un cargo avec une imprimante 3D. Le fait que vous puissiez envoyer des imprimantes 3D à l'aide de cargos ne signifie pas qu'il puisse y avoir une sorte de comparaison entre elles.

Prendre plaisir!

J'espère que cela a clarifié certains concepts.

1
Silvarion