PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Mettre à jour plusieurs colonnes commençant par une chaîne spécifique

Vous avez besoin de SQL dynamique pour cela. Vous devez donc être prêt à faire face à une éventuelle injection SQL.

Requête de base

La requête de base pour générer la commande DML nécessaire peut ressembler à ceci :

SELECT format('UPDATE tbl SET (%s) = (%s)'
               ,string_agg (quote_ident(attname), ', ')
               ,string_agg ('NULL', ', ')
             )
FROM   pg_attribute
WHERE  attrelid = 'tbl'::regclass
AND    NOT attisdropped 
AND    attnum > 0
AND    attname ~~ 'foo_%';

Renvoie :

UPDATE tbl SET (foo_a, foo_b, foo_c) = (NULL, NULL, NULL);
  • J'utilise la "syntaxe de liste de colonnes " de UPDATE pour raccourcir le code et simplifier la tâche.

  • J'interroge les catalogues système au lieu de schéma d'information car ce dernier, tout en étant standardisé et garanti portable sur toutes les versions majeures, est également notoirement lent et parfois peu maniable. Il y a des avantages et des inconvénients, nous en avons discuté plusieurs fois ici sur SO. Recherchez les mots-clés pour plus d'informations.

  • quote_ident() pour les noms de colonnes empêche l'injection SQL et est également nécessaire pour tous noms de colonnes non standard.

  • Vous avez omis de mentionner votre version de Postgres. La fonction d'agrégation string_agg() nécessite 9.0+.

Automatisation complète avec la fonction PL/pgSQL

CREATE OR REPLACE FUNCTION f_update_cols(_tbl regclass, _col_pattern text
                                        , OUT row_ct int, OUT col_ct int)
  RETURNS record AS
$func$
DECLARE
   _sql text;
BEGIN
   SELECT format('UPDATE tbl SET (%s) = (%s)'
                 ,string_agg (quote_ident(attname), ', ')
                 ,string_agg ('NULL', ', ')
                )
         ,count(*)::int
   INTO   _sql, col_ct
   FROM   pg_attribute
   WHERE  attrelid = _tbl
   AND    NOT attisdropped         -- no dropped columns
   AND    attnum > 0               -- no system columns
   AND    attname ~~ _col_pattern; -- only columns matching pattern

   -- RAISE NOTICE '%', _sql;      -- output generated SQL for debugging
   EXECUTE _sql;

   GET DIAGNOSTICS row_ct = ROW_COUNT;
END
$func$  LANGUAGE plpgsql;

COMMENT ON FUNCTION f_update_cols(regclass, text)
 IS 'Updates all columns of table _tbl ($1)
that match _col_pattern ($2) in a LIKE expression.
Returns the count of columns (col_ct) and rows (row_ct) affected.';

Appel :

SELECT * FROM f_update_cols('myschema.tbl', 'foo%');
  • Pour rendre la fonction plus pratique, elle renvoie des informations comme décrit dans le commentaire. En savoir plus sur l'obtention du statut du résultat dans plpgsql dans le manuel.

  • J'utilise la variable _sql pour contenir la chaîne de requête, afin que je puisse collecter le nombre de colonnes trouvées (col_ct ) dans la même requête.

  • Le type d'identifiant d'objet regclass est le moyen le plus efficace d'éviter automatiquement l'injection SQL (et de nettoyer les noms non standard) pour le nom de la table également. Vous pouvez utiliser des noms de table qualifiés par le schéma pour éviter les ambiguïtés. Je vous conseillerais de le faire si vous avez plusieurs schémas dans votre base de données ! Plus de détails dans cette question connexe :
    Nom de table en tant que paramètre de fonction PostgreSQL

-> Démo SQLfiddle .