Utiliser CASE pour sélectionner des colonnes dans la requête UPDATE?

13

Je peux utiliser CASEpour choisir les colonnes à afficher dans une SELECTrequête (Postgres), comme ceci:

SELECT CASE WHEN val = 0 THEN column_x
            WHEN val = 1 THEN column_y
            ELSE 0
       END AS update, ...

Est-ce que quelque chose de similaire est possible lors de l'exécution d'une UPDATErequête dans Postgres (c'est-à-dire choisir les colonnes à mettre à jour)? Je suppose que non, car je n'ai rien trouvé à ce sujet, mais peut-être que quelqu'un a une alternative intelligente (en plus d'utiliser une procédure ou de mettre à jour chaque colonne en utilisant un CASEpour déterminer si la valeur de la colonne doit être affectée à une nouvelle valeur ou simplement réaffecter l'existant valeur). S'il n'y a pas d'alternative facile, je l'accepterai bien sûr comme réponse également.

Information supplémentaire : dans mon cas, j'ai 14 colonnes potentielles qui peuvent être mises à jour, une seule étant mise à jour par ligne correspondante (le tableau à mettre à jour est joint à un autre dans la requête). Le nombre de lignes à mettre à jour variera très probablement, pourrait être des dizaines ou des centaines. Je pense que des indices sont en place pour les conditions d'adhésion.

newenglander
la source

Réponses:

26

Si vous spécifiez qu'une colonne doit être mise à jour, elle sera toujours mise à jour, mais vous pouvez modifier la valeur que vous entrez conditionnellement et remettre les valeurs d'origine en fonction de vos conditions. Quelque chose comme:

UPDATE some_table
SET    column_x = CASE WHEN should_update_x THEN new_value_for_x ELSE column_x END
     , column_y = CASE WHEN should_update_y THEN new_value_for_y ELSE column_y END
     , column_z = CASE WHEN should_update_z THEN new_value_for_z ELSE column_z END
FROM   ...

Donc, si les conditions ne sont pas bonnes pour une mise à jour d'une colonne particulière, il vous suffit de renvoyer sa valeur actuelle.

Prenez note que chaque ligne assortie va voir une mise à jour (même si toutes les colonnes finissent par obtenir ensemble les valeurs qu'ils ont déjà) , à moins que vous porte explicitement cette circonstance en vous le filtrage des clauses sur et où, ce qui pourrait être un problème de performance (il y aura être une écriture, les index seront mis à jour, les déclencheurs appropriés se déclencheront, ...) s'ils ne sont pas atténués.

David Spillett
la source
Merci pour le conseil sur tout ce qui est mis à jour, si c'est lent, je peux prendre la suggestion de @Colin 't Hart d'avoir plusieurs déclarations de mise à jour.
newenglander
Vous pouvez atténuer ce problème en vous assurant que vos clauses ON et WHERE filtrent toutes les lignes où aucune modification n'est nécessaire, mais cela peut signifier la répétition de toutes vos conditions à la fois dans la clause SET et la clause WHERE (sauf s'il existe une vérification globale plus simple qui est 100% équivalent à toutes ces conditions combinées). À ce stade, cette méthode peut être encore plus efficace, mais la méthode des mises à jour multiples peut être plus facile à maintenir.
David Spillett
Sachez également que la mise à jour d'une colonne avec la même valeur entraînera la création d'un rétablissement
Colin 't Hart
@Colin: Oui, toute mise à jour passera par le journal des transactions de la base de données, y compris les mises à jour qui sont essentiellement NoOps en raison de la mise à jour des champs pour avoir les mêmes valeurs qu'auparavant. En plus de la possibilité d'un problème de performances immédiat, cela pourrait être un facteur important si vous utilisez la réplication, les sauvegardes différentielles, l'envoi de journaux, etc., car les opérations de mise à jour des lignes supplémentaires augmenteront l'espace / la bande passante nécessaire pour celles-ci.
David Spillett
Merci à vous deux pour les conseils, dans mon cas, la déclaration de mise à jour unique a bien fonctionné.
newenglander
5

Combien de combinaisons différentes de colonnes mettre à jour avez-vous? Combien de lignes de la table entière seront mises à jour? Des index sont-ils en place pour un accès rapide aux lignes à mettre à jour?

En fonction des réponses à ces questions, vous pourrez peut-être exécuter plusieurs instructions de mise à jour, une pour chaque colonne que vous souhaitez mettre à jour et placer la condition sur la valeur de cette colonne dans la clause where de la mise à jour afin que zéro ligne soit mise à jour si cette colonne a la mauvaise valeur.

Essayez de penser basé sur un ensemble, ne supposez pas que la mise à jour doit mettre à jour une seule ligne trouvée par la clé primaire.

Colin 't Hart
la source
Merci d'avoir répondu. J'ai ajouté plus d'informations à ma question, j'espère que c'est compréhensible. C'est une bonne alternative avec plusieurs instructions de mise à jour (je préfère une instruction de mise à jour, mais je vois qu'il y a un avantage).
newenglander
C'est peut-être la réponse que je cherche, mais voulez-vous mettre mise à jour column_x = new_value_for_x où @val = 0. Je peux expérimenter quelque chose comme ça. Ça a l'air drôle. N'est pas plus facile à faire si val = 0 commence la mise à jour column_x = new_value_for_x etc.
CashCow
-1
update Practicing  -- table you will be updating
 set email = case -- column you will be updating
    when FName = 'Glenn' then '[email protected]'
       when FName = 'Riddick' then '[email protected]'
       when FName = 'Jeffrey' then 'sorcerer@wizcom'
       else email
    end
user134695
la source
Cette mise à jour ne concerne qu'une seule colonne, tandis que le demandeur souhaite savoir comment mettre à jour différentes colonnes en fonction des conditions.
Colin 't Hart