UPDATE — mettre à jour les lignes d'une table
[ WITH [ RECURSIVE ]requête_with
[, ...] ] UPDATE [ ONLY ]nom_table
[ * ] [ [ AS ]alias
] SET {nom_colonne
= {expression
| DEFAULT } | (nom_colonne
[, ...] ) = [ ROW ] ( {expression
| DEFAULT } [, ...] ) | (nom_colonne
[, ...] ) = (sous-SELECT
) } [, ...] [ FROMelement_from
[, ...] ] [ WHEREcondition
| WHERE CURRENT OFnom_curseur
] [ RETURNING { * |expression_sortie
[ [ AS ]nom_sortie
] } [, ...] ]
UPDATE
modifie les valeurs des colonnes spécifiées pour
toutes les lignes qui satisfont la condition. Seules les colonnes à
modifier doivent être mentionnées dans la clause
SET
; les autres colonnes conservent leur
valeur.
Il existe deux façons de modifier le contenu d'une table à partir d'informations
contenues dans d'autres tables de la base de données : à l'aide de
sous-requêtes ou en spécifiant des tables supplémentaires dans la clause
FROM
. Le contexte permet de décider de la technique la plus appropriée.
La clause RETURNING
optionnelle fait que
UPDATE
calcule et renvoie le(s) valeur(s) basée(s) sur
chaque ligne en cours de mise à jour. Toute expression utilisant les
colonnes de la table et/ou les colonnes d'autres tables mentionnées
dans FROM
peut être calculée. La syntaxe de la liste
RETURNING
est identique à celle de la commande
SELECT
.
L'utilisateur doit posséder le droit UPDATE
sur la table,
ou au moins sur les colonnes listées pour la mise à jour. Vous devez aussi
avoir le droit SELECT
sur toutes les colonnes dont les
valeurs sont lues dans les
expression
s ou
condition
.
requête_with
La clause WITH
vous permet de spécifier une ou
plusieurs sous-requêtes qui peuvent être référencées par nom dans la
requêteUPDATE
. Voir Section 7.8 et
SELECT pour les détails.
nom_table
Le nom de la table à mettre à jour (éventuellement qualifié du nom du
schéma). Si ONLY
est indiqué avant le nom de la table,
les lignes modifiées ne concernent que la table nommée. Si
ONLY
n'est pas indiquée, les lignes modifiées font
partie de la table nommée et de ses tables filles. En option,
*
peut être ajouté après le nom de la table pour
indiquer explicitement que les tables filles doivent être inclues.
alias
Un nom de substitution pour la table cible. Quand un alias est fourni,
il cache complètement le nom réel de la table. Par exemple, avec
UPDATE foo AS f
, le reste de l'instruction
UPDATE
doit référencer la table avec
f
et non plus foo
.
nom_colonne
Le nom d'une colonne dans nom_table
.
Le nom de la colonne peut être qualifié avec un nom de sous-champ ou un
indice de tableau, si nécessaire. Ne pas inclure le nom de la table dans
la spécification d'une colonne cible -- par exemple,
UPDATE nom_table SET nom_table.col = 1
est invalide.
expression
Une expression à affecter à la colonne. L'expression peut utiliser les anciennes valeurs de cette colonne et d'autres colonnes de la table.
DEFAULT
Réinitialise la colonne à sa valeur par défaut (qui vaut NULL si aucune expression par défaut ne lui a été affectée). Une colonne d'identité sera configurée à une nouvelle valeur générée par la séquence associée. Pour une colonne générée, spécifier ceci est permis mais ne fait que surligner le comportement normal du calcul de la colonne à partir de l'expression.
sous-SELECT
Une sous-requête SELECT
qui produit autant de
colonnes en sortie que de colonnes comprises dans la liste entre
parenthèses la précédant. La sous-requête doit ne renvoyer qu'une seule
ligne lors de son exécution. Si elle renvoie une seule ligne, les
valeurs des colonnes du résultat sont affectées aux colonnes cibles. Si
elle ne renvoie aucune ligne, des valeurs NULL sont affectées aux
colonnes cibles. La sous-requête peut faire référence aux anciennes
valeurs de la ligne en cours de mise à jour.
eelment_from
Une expression de table, qui permet aux colonnes des autres tables
d'apparaître dans la condition WHERE
et dans les
expressions de mise à jour. Cela utilise la même syntaxe que le FROM
d'une instruction
SELECT
; par exemple, un alias peut être indiqué
pour le nom de la table. Ne répétez pas la table cible dans un
element_from
, sauf si vous souhaitez faire un
jointure sur elle-même (auquel cas elle doit apparaitre avec un alias
dans element_from
).
condition
Une expression qui renvoie une valeur de type boolean
.
Seules les lignes pour lesquelles cette expression renvoie
true
sont mises à jour.
nom_curseur
Le nom du curseur à utiliser dans une condition WHERE CURRENT
OF
. La ligne à mettre à jour est la dernière récupérée à
partir de ce curseur. Le curseur doit être une requête sans regroupement
sur la table cible de l'UPDATE
.
Notez que WHERE CURRENT OF
ne peut pas être
spécifié avec une condition booléenne. Voir
DECLARE pour plus
d'informations sur l'utilisation des curseurs avec
WHERE CURRENT OF
.
expression_sortie
Une expression à calculer et renvoyée par la commande UPDATE
après chaque mise à jour de ligne. L'expression peut utiliser tout nom de
colonne de la table nommée nom_table
ou des
tables listées dans le FROM
.
Indiquez *
pour que toutes les colonnes soient renvoyées.
nom_sortie
Un nom à utiliser pour une colonne renvoyée.
En cas de succès, une commande UPDATE
renvoie un message
de la forme
UPDATE total
total
est le nombre de
lignes mises à jour, en incluant les lignes qui correspondent au filtre mais
dont la valeur des colonnes ne change pas. Notez que le nombre peut
être inférieur au nombre de lignes filtrées par la
condition
quand certaines
mises à jour sont supprimées par un trigger BEFORE UPDATE
.
S'il vaut 0, aucune ligne n'a été mise à jour par cette requête
(ce qui n'est pas considéré comme une erreur).
Lorsqu'une clause FROM
est précisée, la table cible est jointe
aux tables mentionnées dans element_from
, et chaque
ligne en sortie de la jointure représente une opération de mise à jour pour
la table cible. Lors de l'utilisation de FROM
, il faut s'assurer
que la jointure produit au plus une ligne en sortie par
ligne à modifier. En d'autres termes, une ligne cible ne doit pas être
jointe à plus d'une ligne des autres tables. Le cas échéant,
seule une ligne de jointure est utilisée pour mettre à jour la
ligne cible, mais il n'est pas possible de prédire laquelle.
À cause de ce manque de déterminisme, il est plus sûr de ne référencer les autres tables qu'à l'intérieur de sous-requêtes. Même si c'est plus difficile à lire et souvent plus lent que l'utilisation d'une jointure.
Dans le cas d'une table partitionnée, mettre à jour une ligne pourrait faire
qu'elle ne satisfait plus la contrainte de partitionnement de la partition
contenante. Dans ce cas, s'il existe une autre partition dans l'arbre de
partition pour laquelle cette ligne satisfait sa contrainte de
partitionnement, alors la ligne est déplacée dans cette partition. Si une
telle partition n'existe pas, une erreur sera levée. Dans les faits, un
mouvement de ligne est en fait une opération DELETE
suivi
d'un INSERT
.
Il est possible qu'une commande UPDATE
ou
DELETE
en concurrence sur la ligne en déplacement
obtienne une erreur d'échec de sérialisation. Supposons que la session 1
réalise un UPDATE
sur une clé de partitionnement alors
que la session 2, pour laquelle cette ligne est visible, réalise un
UPDATE
ou un DELETE
sur cette ligne.
Dans ce cas, l'opération UPDATE
ou
DELETE
de la session 2 détectera le déplacement de ligne
et renverra une erreur d'échec de sérialisation (qui renvoie toujours le
code SQLSTATE '40001'). Les applications pourraient souhaiter tenter de
nouveau la transaction si cela arrive. Dans le cas inhabituel où la table
n'est pas partitionné ou qu'il n'y a pas de mouvement de ligne, la session
2 aura identifié la ligne nouvellement mise à jour et continué l'opération
UPDATE
/DELETE
sur cette nouvelle
version de ligne.
Notez que, bien que les lignes puissent être déplacées des partitions locales vers une partition distante (fournie par le foreign data wrapper qui supporte le déplacement de lignes), elles ne peuvent pas être déplacées d'une partition distante vers une autre partition.
Une tentative de déplacer une ligne d'une partition à une autre échouera
si une clé étrangère référence directement un ancêtre de la partition source
qui n'est pas le même que l'ancêtre mentionné dans la requête
UPDATE
.
Si la commande UPDATE
contient une clause
RETURNING
, le résultat sera similaire à celui d'une
instruction SELECT
contenant les colonnes et les valeurs
définies dans la liste RETURNING
, à partir de la liste
des lignes mises à jour par la commande, comme la
possibilité d'utiliser la clause WITH
avec la commande
UPDATE
.
Changer le mot Drame
en Dramatique
dans la colonne
genre
de la table films
:
UPDATE films SET genre = 'Dramatique' WHERE genre = 'Drame';
Ajuster les entrées de température et réinitialiser la précipitation à sa
valeur par défaut dans une ligne de la table
temps
:
UPDATE temps SET temp_basse = temp_basse+1, temp_haute = temp_basse+15, prcp = DEFAULT WHERE ville = 'San Francisco' AND date = '2005-07-03';
Réaliser la même opération et renvoyer les lignes mises à jour :
UPDATE temps SET temp_basse = temp_basse+1, temp_haute = temp_basse+15, prcp = DEFAULT WHERE ville = 'San Francisco' AND date = '2003-07-03' RETURNING temp_basse, temp_haute, prcp;
Utiliser une autre syntaxe pour faire la même mise à jour :
UPDATE temps SET (temp_basse, temp_haute, prcp) = (temp_basse+1, temp_basse+15, DEFAULT) WHERE ville = 'San Francisco' AND date = '2003-07-03';
Incrémenter le total des ventes de la personne qui gère le compte d'Acme
Corporation, à l'aide de la clause FROM
:
UPDATE employes SET total_ventes = total_ventes + 1 FROM comptes WHERE compte.nom = 'Acme Corporation' AND employes.id = compte.vendeur;
Réaliser la même opération en utilisant une sous-requête dans la clause
WHERE
:
UPDATE employes SET total_ventes = total_ventes + 1 WHERE id = (SELECT vendeur FROM comptes WHERE nom = 'Acme Corporation');
Mettre à jour les noms du contat dans la table comptes pour correspondre au vendeur actuellement affecté :
UPDATE comptes SET (prenom_compte, nom_compte) = (SELECT prenom, nom FROM vendeurs WHERE vendeurs.id = comptes.id_vendeur);
Un résultat similaire peut être obtenu avec une jointure :
UPDATE comptes SET prenom_contact = prenom, nom_contact = nom FROM vendeurs WHERE vendeurs.id = comptes.id_vendeur;
Néanmoins, la deuxième requête pourrait donner des résultats inattendus si
vendeurs
.id
n'est pas
une clé unique alors que la première requête garantie la levée d'une erreur
si plusieurs id
correspondent. De plus, s'il n'y
a pas de correspondance pour un certain
comptes
.id_vendeur
, la
première requête configurera les champs correspondants à NULL alors que la
deuxième requête ne mettra pas du tout la ligne à jour.
Mettre à jour les statistiques dans une table de résumé pour correspondre aux données actuelles :
UPDATE resumes s SET (somme_x, somme_y, moyenne_x, moyenne_y) = (SELECT sum(x), sum(y), avg(x), avg(y) FROM donnees d WHERE d.id_groupe = s.id_groupe);
Tenter d'insérer un nouvel élément dans le stock avec sa quantité. Si l'élément existe déjà, mettre à jour le total du stock de l'élément. Les points de sauvegarde sont utilisés pour ne pas avoir à annuler l'intégralité de la transaction en cas d'erreur :
BEGIN; -- autres opérations SAVEPOINT sp1; INSERT INTO vins VALUES('Chateau Lafite 2003', '24'); -- A supposer que l'instruction ci-dessus échoue du fait d'une violation de clé -- unique, les commandes suivantes sont exécutées : ROLLBACK TO sp1; UPDATE vins SET stock = stock + 24 WHERE nomvin = 'Chateau Lafite 2003'; -- continuer avec les autres opérations, et finir COMMIT;
Modifier la colonne genre
de la table
films
dans la ligne où le curseur
c_films
est actuellement positionné :
UPDATE films SET genre = 'Dramatic' WHERE CURRENT OF c_films;
Les mises à jour affectant de nombreuses lignes peuvent avoir des effets
négatifs sur les performance d'un système, comme la fragmentation des
tables, un retard de réplication plus important, et des contentions plus
longues sur les verrous. Dans de telles situations, il est sensé de
répartir l'opération en plusieurs morceaux, potentiellement avec une
opération VACUUM
sur la table entre chaque morceau.
Bien qu'il n'existe pas de clause LIMIT
pour
UPDATE
, il est possible d'obtenir un effet similaire
en utilisant Common Table Expressionet
une jointure sur soi-même. Avec la méthode d'accès aux tables standard de
PostgreSQL, une jointure sur soi à partir de
la colonne système ctid
est très efficace :
WITH exceeded_max_retries AS ( SELECT w.ctid FROM work_item AS w WHERE w.status = 'active' AND w.num_retries > 10 ORDER BY w.retry_timestamp FOR UPDATE LIMIT 5000 ) UPDATE work_item SET status = 'failed' FROM exceeded_max_retries AS emr WHERE work_item.ctid = emr.ctid;
Cette commande devra être répétée jusqu'à ce qu'il n'y ait plus de lignes
à mettre à jour. L'utilisation d'une clause ORDER BY
permet à la commande de prioriser les lignes à mettre à jour ;
elle permet aussi d'éviter des deadlocks avec d'autres opérations de
mise à jour si elles utilisent le même tri. Si la contention des
verrous devient un problème, alors la clause SKIP LOCKED
peut être ajoutée à la CTE pour empêcher plusieurs
commandes de mettre à jour la même ligne. Néanmoins, un
UPDATE
final sans SKIP LOCKED
ou
LIMIT
sera alors nécessaire pour s'assurer qu'aucune ligne n'a été oubliée.
Cette commande est conforme au standard SQL, à
l'exception des clauses FROM
et RETURNING
qui sont des extensions PostgreSQL.
D'autres systèmes de bases de données offrent l'option
FROM
dans laquelle la table cible est supposée être à nouveau
indiquée dans le FROM
.
PostgreSQL n'interprète pas la clause FROM
ainsi. Il est important d'en tenir compte lors du portage d'applications qui
utilisent cette extension.
D'après le standard, la valeur source pour une sous-liste de noms de
colonnes peut être toute expression de ligne renvoyant le bon nombre de
colonnes. PostgreSQL autorise seulement la
valeur source à être un constructeur de ligne
ou un sous-SELECT
. Une valeur
mise à jour pour une colonne individuelle peut être spécifiée en tant que
DEFAULT
dans le cas d'une liste d'expressions, mais pas à
l'intérieur d'un sous-SELECT
.